49 Commits

Author SHA1 Message Date
zhangshuai
e675144a6c feat: ASW-40 application 接口开发 2024-08-21 10:19:01 +08:00
zhangshuai
c57ade402f fix: pcap webshark upload error 响应错误码 2024-08-15 17:54:17 +08:00
zhangshuai
f14763512e fix: pcap webshark upload error 响应错误码 2024-08-15 17:54:07 +08:00
zhangshuai
b480734c94 feat: pcap 增加 unparse2session 接口 2024-08-15 17:30:13 +08:00
zhangshuai
1c35979b24 fix: pcap 响应 summary 内容 2024-08-15 16:55:16 +08:00
zhangshuai
68ccb87c76 fix:调整 webshark 接口返回值 2024-08-15 16:18:28 +08:00
zhangshuai
a98566f5d5 fix:调整 webshark 默认端口 2024-08-15 15:12:30 +08:00
zhangshuai
634166c4b3 feat: ASW-38 webshark 访问接口开发 2024-08-15 15:09:43 +08:00
shizhendong
ecb57f6c6c feat: session 增加 stream_id,stream_url
1. pcap 新增 summary 统计信息
2024-08-13 14:08:33 +08:00
zhangshuai
58b38fbe91 fix: 调整 application developer 为 provider 2024-08-12 15:22:55 +08:00
zhangshuai
582856c066 fix: 取消展示更新记录链接 2024-08-12 15:07:35 +08:00
shizhendong
76e52d91e9 fix: 调整 application explore 接口相应内容 2024-08-12 14:07:09 +08:00
zhangshuai
f3d048c240 fix: application 添加 packageName website developer 属性 2024-08-09 17:21:29 +08:00
shizhendong
c3d9750bdc feat: ASW-30 新增 Application 分析重定向接口 2024-08-08 17:10:28 +08:00
zhangshuai
afb19a4326 fix: 调整 application path 2024-08-08 15:23:47 +08:00
zhangshuai
519b6e1c2d fix: 修改 index.html 路径 2024-08-08 14:52:22 +08:00
zhangshuai
e8b6e902c2 fix: 修改 index.html 路径 2024-08-08 14:13:41 +08:00
zhangshuai
a32327ad22 feat: ASW-31 asw controller 增加 GUIHistoryRouterFilter 2024-08-08 13:40:50 +08:00
zhangshuai
f7caf6262c feat: ASW-29 Application 恢复接口开发 2024-08-07 15:50:02 +08:00
zhangshuai
8932734ef6 fix: application log order by op_version desc 2024-08-07 11:11:29 +08:00
zhangshuai
3265d73dfe fix: application 页面添加 history 与 compare 按钮 2024-08-07 10:29:19 +08:00
zhangshuai
e88dc879e2 fix: application 不校验 description 2024-08-07 10:23:00 +08:00
zhangshuai
11a4000eb2 fix: 只允许applications,pcaps 菜单可见 2024-08-07 10:06:53 +08:00
zhangshuai
172bd9e890 fix: application 接口返回 user 对象 2024-08-06 18:15:39 +08:00
zhangshuai
d5f6aef383 fix: application 接口返回 user 对象 2024-08-06 18:15:11 +08:00
zhangshuai
875eb83d6e feat: ASW-26 pcap download 接口开发 2024-08-06 15:33:37 +08:00
zhangshuai
b6cbe6094f fix: application name 与 description 非空校验 2024-08-06 10:47:12 +08:00
zhangshuai
9a21b440b0 fix: update application.button order 2024-08-05 15:07:24 +08:00
zhangshuai
ba87a497bb feat: ASW-22 workspace 接口开发
1.workspace 接口开发
2.新增 application delete, edit, add 按钮
2024-08-05 15:01:35 +08:00
shizhendong
aaff071420 fix: 修复 pcap 重复上传时删除原始文件问题 2024-08-05 10:10:48 +08:00
shizhendong
a837a160f9 fix: 调整 pcap 文件解析 index_name= workspace-{workspace.name}-{pcap.md5} 2024-08-02 18:27:22 +08:00
shizhendong
af0d0e55ca feat: 集成 satoken jwt
1. 登录成功后返回 jwt token
2024-08-02 17:30:19 +08:00
zhangshuai
4b53e78124 fix: 删除 Signature 菜单,菜单补充 icon ,调整菜单 i18n 2024-08-02 14:52:56 +08:00
zhangshuai
2c8b299735 feat: application 详情接口增加 version 参数 2024-08-01 17:48:42 +08:00
zhangshuai
96483dc432 feat: ASW-21 用户权限查询接口接口开发 2024-08-01 17:09:29 +08:00
shizhendong
d9ec686bc7 feat: ASW-14 新增 pcap 解析接口 2024-07-30 21:10:39 +08:00
zhangshuai
17328600aa feat: ASW-18 application log 查询接口开发 2024-07-30 17:10:29 +08:00
zhangshuai
6b78f8e61a feat: ASW-18 application 接口开发 2024-07-30 16:02:32 +08:00
shizhendong
b5af3de27d fix: 补充提交 2024-07-30 14:00:25 +08:00
shizhendong
4a2d7f9adf feat: 调整 pcap 增删改查接口 2024-07-30 13:53:59 +08:00
shizhendong
4f8084eaf8 feat: ASW-10 新增 pcap 相关接口 2024-07-23 11:37:47 +08:00
shizhendong
5e52c711bc fix: 补充 heartbeat 接口更新 runer.last_heartbeat_timestamp value 2024-07-19 17:52:42 +08:00
zhangshuai
c0623e8ca5 feat: update gitlab cicd 2024-07-18 11:38:38 +08:00
zhangshuai
da051af99f feat: 新增 gitlab cicd 2024-07-18 10:31:21 +08:00
shizhendong
79146845b9 feat: ASW-8 新增 Runner 相关接口 2024-07-16 17:38:32 +08:00
shizhendong
5a4c15b00a feat: ASW-7 新增 Package 相关接口 2024-07-12 14:27:31 +08:00
shizhendong
9e5709d1f1 feat: ASW-6 新增 JOB 相关接口 2024-07-10 18:05:23 +08:00
shizhendong
e85811b805 refactor: 统一 sys_i18n 更新人,更新时间戳字段名称 2024-06-26 10:46:15 +08:00
shizhendong
58431f9053 feat: ASW-2 新增项目基础接口
1. 系统认证接口
2. 用户相关接口
3. i18n 国际化接口
2024-06-26 10:29:24 +08:00
120 changed files with 7620 additions and 202 deletions

82
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,82 @@
# 定义全局docker镜像
image: git.mesalab.cn:7443/nezha/nz-build-env:1.6
# 定义全局变量
variables:
MINIO_HOST: 'http://192.168.40.48:2020/'
MINIO_USER: 'admin'
MINIO_PWD: "Nezha@02!"
MAVEN_REPO: "/etc/maven/repository/"
# mariadb 数据库定义(非必须)
MYSQL_DATABASE: "test"
# mariadb 密码配置(必须),注意变量名是 MYSQL_ROOT_PASSWORD
MYSQL_ROOT_PASSWORD: '111111'
# 定义全局依赖的docker服务即 这条流水线 pipeline 中的任务都用这里的服务
services:
- mariadb:10.2.14
# 开始执行脚本前所需执行脚本
before_script:
- echo "begin ci"
# 脚本执行完后的钩子,执行所需脚本
after_script:
- echo "end ci"
# 该ci pipeline适合的场景按照定义的顺序执行任务
stages:
- build
- test
# paths主要是来指定需要被缓存的文件路径需要特别指出的是这里的 paths 是相对路径是相对于gitlab中项目目录的路径也就是说被缓存的文件都是在项目目录之内的
# maven setting /usr/share/maven/conf/settings.xml
cache:
paths:
- $MAVEN_REPO
# 定义的任务
build_rpm:
stage: build
# 所需执行的脚本
script:
- env | sort
- pwd
- export FILE_NAME=$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA.tar.gz
- mvn clean install -Dmaven.test.skip=true
- cd ./target
- tar -zcvf $FILE_NAME asw-controller.jar
# 将 文件 上传到 minio
- mc alias set asw $MINIO_HOST $MINIO_USER $MINIO_PWD
- mc cp $FILE_NAME asw/release/asw-controller/$FILE_NAME
- cd ../
# 在哪个分支上可用
only:
- /^rel-.*$/i
# 指定哪个ci runner跑该工作
tags:
- asw
# 定义的任务
dev_build:
stage: test
# 所需执行的脚本
script:
- env | sort
- pwd
- export FILE_NAME=$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA.tar.gz
- mvn clean install -Dmaven.test.skip=true
- cd ./target
# - "git log -100 --pretty=format:'%ad : %s' > git-log.html"
# - tar -zcvf $FILE_NAME asw-controller.jar git-log.html
- tar -zcvf $FILE_NAME asw-controller.jar
- md5sum $FILE_NAME > $CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-latest.tar.gz.md5sum.txt
# 将 文件 上传到 minio
- mc alias set asw $MINIO_HOST $MINIO_USER $MINIO_PWD
- mc cp $FILE_NAME asw/ci-cd/asw-controller/$FILE_NAME
- mc cp $FILE_NAME asw/ci-cd/asw-controller/$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-latest.tar.gz
- mc cp $CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-latest.tar.gz.md5sum.txt asw/ci-cd/asw-controller/$CI_PROJECT_NAME-$CI_COMMIT_REF_NAME-latest.tar.gz.md5sum.txt
- cd ../
# 在哪个分支上可用
only:
- /^dev-.*$/i
# 指定不执行的标签
except:
- schedules
# 指定哪个ci runner跑该工作
tags:
- asw

36
pom.xml
View File

@@ -79,6 +79,14 @@
<artifactId>sa-token-spring-boot3-starter</artifactId> <artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.37.0</version> <version>1.37.0</version>
</dependency> </dependency>
<!-- Sa-Token 整合 jwt -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-jwt</artifactId>
<version>1.37.0</version>
</dependency>
<dependency> <dependency>
<groupId>cn.hutool</groupId> <groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId> <artifactId>hutool-all</artifactId>
@@ -143,6 +151,34 @@
<artifactId>simplemagic</artifactId> <artifactId>simplemagic</artifactId>
<version>1.16</version> <version>1.16</version>
</dependency> </dependency>
<!--Feign client支持-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.51</version>
</dependency>
<!--opensearch-->
<dependency>
<groupId>org.opensearch.client</groupId>
<artifactId>opensearch-java</artifactId>
<version>2.12.0</version>
</dependency>
<dependency>
<groupId>org.opensearch.client</groupId>
<artifactId>opensearch-rest-client</artifactId>
<version>2.12.0</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -0,0 +1,51 @@
package net.geedge.asw.common.config;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
@Component
public class GUIHistoryRouterFilter extends OncePerRequestFilter {
@Value("${router.prefixes:/static/,/api/}")
private String pathPrefixes;
private final ResourceLoader resourceLoader;
public GUIHistoryRouterFilter(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
List<String> prefixes = Arrays.asList(pathPrefixes.split(","));
String path = request.getRequestURI();
boolean matches = prefixes.stream().anyMatch(path::startsWith);
if (!matches) {
// If the path does not start with any of the specified prefixes, return index.html
Resource resource = resourceLoader.getResource("file:./public/index.html");
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
Files.copy(resource.getFile().toPath(), response.getOutputStream());
return;
}
// If the path matches any of the prefixes, continue the filter chain
filterChain.doFilter(request, response);
}
}

View File

@@ -0,0 +1,122 @@
package net.geedge.asw.common.config;
import cn.hutool.log.Log;
import jakarta.annotation.PostConstruct;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysI18nEntity;
import net.geedge.asw.module.sys.service.ISysI18nService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.support.AbstractMessageSource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@Component
public class I18nConfig extends AbstractMessageSource implements ResourceLoaderAware {
private static final Log log = Log.get();
@Autowired
private ISysI18nService sysI18nService;
/**
* 国际化缓存
*/
public static final Map<String, Map<String, String>> I18N_CACHE = new HashMap<>();
@PostConstruct
public void init() {
this.reload();
}
/**
* 重新将数据库中的国际化配置加载
*/
public void reload() {
I18N_CACHE.clear();
}
protected ResourceLoader resourceLoader;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = (resourceLoader == null ? new DefaultResourceLoader() : resourceLoader);
}
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
String msg = this.getSourceFromCache(code, locale);
MessageFormat messageFormat = new MessageFormat(msg, locale);
return messageFormat;
}
@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return this.getSourceFromCache(code, locale);
}
/**
* 从缓存中取出国际化配置对应的数据 或者从父级获取
*
* @param code
* @param locale
* @param param
* @return
*/
public String getSourceFromCache(String code, Locale locale, Object... param) {
String language = locale.getLanguage();
if (T.ObjectUtil.isEmpty(I18N_CACHE)) {
this.loadAllMessageResourcesFromDB();
}
Map<String, String> props = I18N_CACHE.get(language);
if (null != props && props.containsKey(code)) {
String msg = props.get(code);
if (T.ObjectUtil.isEmpty(param)) {
return msg;
}
return MessageFormat.format(msg, param);
} else {
try {
if (null != this.getParentMessageSource()) {
return this.getParentMessageSource().getMessage(code, param, locale);
}
} catch (Exception e) {
log.error(e);
}
return code;
}
}
/**
* 从数据库中获取所有国际化配置
*/
public void loadAllMessageResourcesFromDB() {
List<SysI18nEntity> list = sysI18nService.list();
if (T.CollUtil.isNotEmpty(list)) {
try {
for (String lang : Constants.LANG_LIST) {
Map<String, String> langMap = I18N_CACHE.get(lang);
langMap = T.ObjectUtil.defaultIfNull(langMap, new HashMap<>());
I18N_CACHE.put(lang, langMap);
List<SysI18nEntity> dataList = list.stream().filter(pojo -> T.StrUtil.equals(lang, pojo.getLang())).collect(Collectors.toList());
for (SysI18nEntity entity : dataList) {
langMap.put(entity.getCode(), entity.getValue());
}
}
} catch (Exception e) {
log.error(e);
}
}
}
}

View File

@@ -0,0 +1,66 @@
package net.geedge.asw.common.config;
import cn.hutool.log.Log;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.T;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.i18n.LocaleChangeInterceptor;
import org.springframework.web.servlet.i18n.SessionLocaleResolver;
import java.util.Locale;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class LocaleConfig implements WebMvcConfigurer {
@Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver slr = new SessionLocaleResolver();
// 默认语言
slr.setDefaultLocale(Locale.of("en"));
return slr;
}
@Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
MyI18nInterceptor lci = new MyI18nInterceptor();
// 参数名
lci.setParamName("Language");
return lci;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(localeChangeInterceptor());
}
}
class MyI18nInterceptor extends LocaleChangeInterceptor {
private static final Log log = Log.get();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
try {
String language = request.getHeader(getParamName());
if (T.ObjectUtil.isNotEmpty(language)) {
Locale locale = parseLocaleValue(language);
LocaleContextHolder.setLocale(locale);
}
} catch (Exception e) {
log.error(e, "[preHandle] [error]");
}
return true;
}
}

View File

@@ -1,17 +1,23 @@
package net.geedge.asw.common.config; package net.geedge.asw.common.config;
import java.util.concurrent.TimeUnit; import cn.dev33.satoken.config.SaTokenConfig;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.jwt.SaJwtTemplate;
import cn.dev33.satoken.jwt.SaJwtUtil;
import cn.dev33.satoken.jwt.StpLogicJwtForStateless;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpLogic;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.jwt.JWT;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import cn.dev33.satoken.config.SaTokenConfig; import java.util.concurrent.TimeUnit;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
public class SaTokenConfigure implements WebMvcConfigurer { public class SaTokenConfigure implements WebMvcConfigurer {
@@ -38,6 +44,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
config.setTokenStyle("simple-uuid"); // token 风格 config.setTokenStyle("simple-uuid"); // token 风格
config.setIsLog(false); // 是否输出操作日志 config.setIsLog(false); // 是否输出操作日志
config.setIsPrint(false); config.setIsPrint(false);
// jwt秘钥
config.setJwtSecretKey("ypCLARItfzxvdVqRwPcwIasdgAkhoubj");
// config.setIsReadCookie(false); // config.setIsReadCookie(false);
} }
@@ -47,7 +55,32 @@ public class SaTokenConfigure implements WebMvcConfigurer {
// 注册 Sa-Token 拦截器,打开注解式鉴权功能 // 注册 Sa-Token 拦截器,打开注解式鉴权功能
registry.addInterceptor(new SaInterceptor(handler -> { registry.addInterceptor(new SaInterceptor(handler -> {
SaRouter.match("/file/**").notMatch("/file/content/*").check(r -> StpUtil.checkLogin()); SaRouter.match("/file/**").notMatch("/file/content/*").check(r -> StpUtil.checkLogin());
SaRouter.match("/sys/**").notMatch("/sys/login").check(r -> StpUtil.checkLogin()); SaRouter.match("/api/v1/**").notMatch("/api/v1/login").check(r -> StpUtil.checkLogin());
})).addPathPatterns("/**"); })).addPathPatterns("/**");
} }
@Bean
public StpLogic getStpLogicJwt() {
// Sa-Token 整合 jwt (Stateless 无状态模式)
return new StpLogicJwtForStateless();
}
/**
* 自定义 SaJwtUtil 生成 token 的算法
*/
@PostConstruct
public void setSaJwtTemplate() {
SaJwtUtil.setSaJwtTemplate(new SaJwtTemplate() {
@Override
public String generateToken(JWT jwt, String keyt) {
// header
jwt.setHeader("alg", "HS256");
jwt.setHeader("typ", "JWT");
// payload
jwt.setPayload("iss", "net.geedge.asw");
return super.generateToken(jwt, keyt);
}
});
}
} }

View File

@@ -0,0 +1,41 @@
package net.geedge.asw.common.config;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Spring Context 工具类
*/
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtils.applicationContext = applicationContext;
}
public static Object getBean(String name) {
return applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> requiredType) {
return applicationContext.getBean(requiredType);
}
public static String getProperty(String key, String defaultValue) {
return applicationContext.getEnvironment().getProperty(key, defaultValue);
}
public static <T> T getBean(String name, Class<T> requiredType) {
return applicationContext.getBean(name, requiredType);
}
public static Class<? extends Object> getType(String name) {
return applicationContext.getType(name);
}
}

View File

@@ -1,18 +1,20 @@
package net.geedge.asw.common.config.exception; package net.geedge.asw.common.config.exception;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.exception.NotLoginException;
import cn.hutool.log.Log; import cn.hutool.log.Log;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R; import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.service.ISysI18nService;
import org.apache.catalina.connector.ClientAbortException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/** /**
* 异常处理器 * 异常处理器
@@ -22,6 +24,9 @@ public class ASWExceptionHandler {
private static final Log log = Log.get(); private static final Log log = Log.get();
@Autowired
private ISysI18nService sysI18nService;
/** /**
* 处理自定义异常 * 处理自定义异常
*/ */
@@ -29,7 +34,19 @@ public class ASWExceptionHandler {
@ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseStatus(value = HttpStatus.BAD_REQUEST)
public R handleDHException(ASWException e, HttpServletRequest request) { public R handleDHException(ASWException e, HttpServletRequest request) {
log.warn(e, "Request uri: {}", request.getRequestURI()); log.warn(e, "Request uri: {}", request.getRequestURI());
return R.error(e.getCode(), e.getMsg()); R r = new R();
r.put("code", e.getCode());
String msg = "";
// by code
if (T.ObjectUtil.isNotEmpty(e.getCode())) {
msg = sysI18nService.queryValueByName(T.StrUtil.toString(e.getCode()), e.getParam());
}
if (T.StrUtil.isEmpty(msg) && (T.ObjectUtil.isEmpty(e.getRCode()) || T.ObjectUtil.equals(msg, e.getRCode().toString()))) {
r.put("msg", e.getMsg());
} else {
r.put("msg", msg);
}
return r;
} }
/** /**
@@ -49,7 +66,8 @@ public class ASWExceptionHandler {
@ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseStatus(value = HttpStatus.BAD_REQUEST)
public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) { public R handleDuplicateKeyException(DuplicateKeyException e, HttpServletRequest request) {
log.error(e, "Request uri: {}", request.getRequestURI()); log.error(e, "Request uri: {}", request.getRequestURI());
return R.error(RCode.SYS_DUPLICATE_RECORD); String msg = sysI18nService.queryValueByName(RCode.SYS_DUPLICATE_RECORD.getCode().toString());
return R.error(RCode.SYS_DUPLICATE_RECORD.getCode(), msg);
} }
@ExceptionHandler(Exception.class) @ExceptionHandler(Exception.class)

View File

@@ -12,12 +12,14 @@ public class ASWException extends RuntimeException {
private String msg = RCode.ERROR.getMsg(); private String msg = RCode.ERROR.getMsg();
private int code = RCode.ERROR.getCode(); private int code = RCode.ERROR.getCode();
private Object[] param = new Object[] {}; private Object[] param = new Object[] {};
private RCode rCode;
public ASWException(RCode rCode) { public ASWException(RCode rCode) {
super(rCode.getMsg()); super(rCode.getMsg());
this.code = rCode.getCode(); this.code = rCode.getCode();
this.msg = rCode.getMsg(); this.msg = rCode.getMsg();
this.param = rCode.getParam(); this.param = rCode.getParam();
this.rCode = rCode;
} }
public ASWException(String msg) { public ASWException(String msg) {

View File

@@ -1,6 +1,7 @@
package net.geedge.asw.common.util; package net.geedge.asw.common.util;
import java.io.File; import java.io.File;
import java.util.List;
public class Constants { public class Constants {
@@ -14,6 +15,15 @@ public class Constants {
*/ */
public static final String TEMP_PATH = System.getProperty("user.dir") + File.separator + "tmp"; public static final String TEMP_PATH = System.getProperty("user.dir") + File.separator + "tmp";
/**
* 国际化语言列表
*/
public static final List<String> LANG_LIST = T.ListUtil.of("en", "zh");
/**
* 工作空间可见性列表
*/
public static final List<String> VISIBILITY_LIST = T.ListUtil.of("public", "private");
} }

View File

@@ -5,7 +5,7 @@ import java.text.MessageFormat;
public enum RCode { public enum RCode {
/** /**
* 10**** : 系统认证 或 通用错误提示 20**** : screen module * 10**** : 系统认证 或 通用错误提示 20**** : sys module
*/ */
ERROR(999, "error"), // 通用错误/未知错误 ERROR(999, "error"), // 通用错误/未知错误
@@ -18,27 +18,56 @@ public enum RCode {
PARAM_CANNOT_EMPTY(100006, "parameter cannot be empty"), // parameter 不能为空 PARAM_CANNOT_EMPTY(100006, "parameter cannot be empty"), // parameter 不能为空
USER_NO_LOGIN(100007, "user not login"), // 用户未登录 USER_NO_LOGIN(100007, "user not login"), // 用户未登录
SYS_RECORD_NOT_FOUND(100008, "record not found"),// 未找到记录 SYS_RECORD_NOT_FOUND(100008, "record not found"),// 未找到记录
USER_ID_CANNOT_EMPTY(100009, "user id cannot be empty"),// 用户 ID 不能为空
ROLE_ID_CANNOT_EMPTY(100010, "role id cannot be empty"),// 权限 ID 不能为空
USER_NOT_EXIST(100011, "user does not exist"),
ROLE_NOT_EXIST(100012, "role does not exist"),
SCREEN_ID_CANNOT_EMPTY(200001, "id cannot be empty"), // Application
APP_ID_CANNOT_EMPTY(201001, "application id cannot be empty"),
APP_NAME_CANNOT_EMPTY(201002, "application name cannot be empty"),
APP_LONGNAME_CANNOT_EMPTY(201003, "application longName cannot be empty"),
APP_PROPERTIES_CANNOT_EMPTY(201004, "application properties cannot be empty"),
APP_SURROGATES_CANNOT_EMPTY(201005, "application surrogates cannot be empty"),
APP_DESCRIPTION_CANNOT_EMPTY(201006, "application description cannot be empty"),
APP_DUPLICATE_RECORD(201007, "application duplicate record"),
APP_NOT_EXIST(201008, "application does not exist"),
APP_PACKAGE_NAME_FORMAT_ERROR(201009, "application package name format error"),
APP_TAGS_FORMAT_ERROR(201010, "application tags format error"),
APP_SIGNATURE_FORMAT_ERROR(201011, "application signature format error"),
APP_SIGNATURE_CONTENT_CANNOT_EMPTY(201012, "application signature content cannot be empty"),
APP_SIGNATURE_NOT_EXIST(201013, "application signature does not exist"),
APP_NOTE_CONTENT_CANNOT_EMPTY(201014, "application note content cannot be empty"),
SCREE_DATASOURCE_DEFAULT_CANNOT_BE_DELETE(300001,"The default data source cannot be deleted."),
SCREE_DATASOURCE_REPEAT(300002,"Screen datasource name duplicate"),
/**
* import // Package
*/ PACKAGE_ID_CANNOT_EMPTY(202001, "package id cannot be empty"),
EXCELFILE_TYPE_ERROR(400001, "The type can only be xlsx, json, csv"), PACKAGE_DESCRIPTION_CANNOT_EMPTY(202002, "package description cannot be empty"),
EXCELFILE_PARSE_ERROR(400002, "Import file resolution failed"),
EXCELFILE_HEADER_TEMPLATE_ERROR(400003,"The header row of the import template is inconsistent with the system template"),
EXCELFILE_HEADER_LANGUAGE_ERROR(400004, "Language must be en, zh or ru"), // Runner
EXCELFILE_IMPORT_FILE_ISNULL(400005, "Import file is null"), RUNNER_ID_CANNOT_EMPTY(301001, "runner id cannot be empty"),
EXCELFILE_HEADER_LANGUAGE_ISNULL(400006, "Language can not be empty"),
EXCELFILE_IMPORT_ERROR(400007, "File import error"),
EXCELFILE_SCHEDULE_TASK_IS_NULL(400008, "Schedule task can not be empty"), // Playbook
EXCELFILE_SCHEDULE_CRON_IS_NULL(400009, "Schedule cron can not be empty"), PLAYBOOK_ID_CANNOT_EMPTY(302001, "playbook id cannot be empty"),
EXCELFILE_SCHEDULE_ENABLE_IS_NULL(400010, "Schedule enable can not be empty"),
EXCELFILE_SCHEDULE_SCRIPT_IS_NULL(400011, "Schedule script can not be empty"),
// Workspace
WORKSPACE_ID_CANNOT_EMPTY(401001, "workspace id cannot be empty"),
WORKSPACE_NAME_CANNOT_EMPTY(401002, "workspace name cannot be empty"),
WORKSPACE_VISIBILITY_CANNOT_EMPTY(401003, "workspace visibility cannot be empty"),
WORKSPACE_USER_CANNOT_EMPTY(401004, "workspace user cannot be empty"),
WORKSPACE_ALREADY_EXISTS(401005, "workspace already exists"),
WORKSPACE_MEMBER_CANNOT_EMPTY(401006, "workspace member cannot be empty"),
WORKSPACE_CANNOT_DELETE(401007, "Built-in workspace cannot be deleted"),
WORKSPACE_VISIBILITY_ERROR(401008, "workspace visibility error"),
WORKSPACE_BUILT_IN(401009, "Built-in workspace cannot be update"),
//PCAP
PCAP_UPLOAD_WEB_SHARK_ERROR(501001, "web shark upload pcap error"),
SUCCESS(200, "success"); // 成功 SUCCESS(200, "success"); // 成功

View File

@@ -1,27 +1,9 @@
package net.geedge.asw.common.util; package net.geedge.asw.common.util;
import java.awt.Graphics; import cn.hutool.core.date.DateTime;
import java.awt.Robot; import cn.hutool.log.Log;
import java.lang.ref.PhantomReference; import com.baomidou.mybatisplus.core.metadata.OrderItem;
import java.lang.ref.Reference; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.Spliterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
@@ -29,10 +11,23 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import java.awt.*;
import com.baomidou.mybatisplus.core.metadata.OrderItem; import java.io.File;
import java.io.UnsupportedEncodingException;
import cn.hutool.core.date.DateTime; import java.lang.ref.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.net.Socket;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class T { public class T {
@@ -315,6 +310,27 @@ public class T {
* @author xiaoleilu * @author xiaoleilu
*/ */
public static class PageUtil extends cn.hutool.core.util.PageUtil { public static class PageUtil extends cn.hutool.core.util.PageUtil {
public static final Integer DEFAULT_PAGENO = 1;
public static final Integer DEFAULT_PAGESIZE = 20;
public static Page getPage(Map<String, Object> params) {
// 分页参数
Integer pageNo = T.MapUtil.getInt(params, "current", DEFAULT_PAGENO);
Integer pageSize = T.MapUtil.getInt(params, "size", DEFAULT_PAGESIZE);
if (pageSize == -1) {
pageNo = 0;
pageSize = Integer.MAX_VALUE;
}
Page page = Page.of(pageNo, pageSize);
String orderBy = T.MapUtil.getStr(params, "orderBy");
if (T.StrUtil.isNotEmpty(orderBy)) {
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
}
return page;
}
public static OrderItem decodeOrderByStr(String orderBy) { public static OrderItem decodeOrderByStr(String orderBy) {
if (cn.hutool.core.util.StrUtil.isBlank(orderBy)) { if (cn.hutool.core.util.StrUtil.isBlank(orderBy)) {
return null; return null;
@@ -1526,4 +1542,37 @@ public class T {
super(value); super(value);
} }
} }
/**
* 获取项目中各种路径
*
* @author ThinkPad
*/
public static class WebPathUtil {
private static final Log log = Log.get();
/**
* 如果已打成jar包则返回jar包所在目录
* 如果未打成jar则返回target所在目录
*
* @return
*/
public static String getClassPath() {
try {
// 项目的编译文件的根目录
String path = URLDecoder.decode(System.getProperty("user.dir"), "utf-8");
log.debug("root path:{}", path);
return path;
} catch (UnsupportedEncodingException e) {
return null;
}
}
public static String getRootPath() {
File file = T.FileUtil.file(WebPathUtil.getClassPath());
return file.getAbsolutePath();
}
}
} }

View File

@@ -387,6 +387,13 @@ public class VerifyUtil {
return this; return this;
} }
public VerifyUtil json(RCode code) {
if (!T.JSONUtil.isTypeJSON(T.StrUtil.toStringOrNull(value))) {
throw ASWException.builder().rcode(code).build();
}
return this;
}
/** /**
* 多参数校验,不能同时为空 * 多参数校验,不能同时为空
* *

View File

@@ -0,0 +1,181 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import net.geedge.asw.module.app.service.ApplicationAttachmentService;
import net.geedge.asw.module.app.service.ApplicationNoteService;
import net.geedge.asw.module.app.service.ApplicationSignatureService;
import net.geedge.asw.module.app.service.IApplicationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/application")
public class ApplicationController {
@Autowired
private IApplicationService applicationService;
@Autowired
private ApplicationSignatureService signatureService;
@Autowired
private ApplicationNoteService noteService;
@Autowired
private ApplicationAttachmentService attachmentService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id, String workspaceId) {
T.VerifyUtil.is(workspaceId).notNull();
ApplicationEntity entity = applicationService.detail(id, workspaceId);
if (T.ObjectUtil.isNull(entity)) {
throw new ASWException(RCode.APP_NOT_EXIST);
}
return R.ok().putData("record", entity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = applicationService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody ApplicationEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getName()).notEmpty(RCode.APP_NAME_CANNOT_EMPTY)
//.and(entity.getSignature()).notEmpty(RCode.APP_SURROGATES_CANNOT_EMPTY)
//.and(entity.getNote()).notEmpty(RCode.APP_PROPERTIES_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
ApplicationEntity applicationEntity = applicationService.saveApplication(entity);
return R.ok().putData("id", applicationEntity.getId());
}
@PutMapping
public R update(@RequestBody ApplicationEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
//.and(entity.getSignature()).notEmpty(RCode.APP_SURROGATES_CANNOT_EMPTY)
//.and(entity.getNote()).notEmpty(RCode.APP_PROPERTIES_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
ApplicationEntity applicationEntity = applicationService.updateApplication(entity);
return R.ok().putData("id", applicationEntity.getId());
}
@PutMapping("/{id}/basic")
public R basic(@PathVariable String id, @RequestBody ApplicationEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
entity.setId(id);
ApplicationEntity app = applicationService.updateBasic(entity);
return R.ok().putData("id", app.getId());
}
@PutMapping("/{applicationId}/signature")
public R updateSignature(@PathVariable("applicationId") String applicationId, @RequestBody ApplicationSignatureEntity signature) {
T.VerifyUtil.is(signature).notNull()
.and(signature.getContent()).notEmpty(RCode.APP_SURROGATES_CANNOT_EMPTY)
.and(signature.getContent()).json(RCode.APP_SURROGATES_CANNOT_EMPTY);
signatureService.saveSignature(signature, applicationId);
return R.ok().putData("id", signature.getId());
}
@PutMapping("/{applicationId}/note")
public R updateNote(@PathVariable("applicationId") String applicationId, @RequestBody ApplicationNoteEntity note) {
T.VerifyUtil.is(note).notNull()
.and(note.getContent()).notEmpty(RCode.APP_NOTE_CONTENT_CANNOT_EMPTY);
noteService.saveNote(note, applicationId);
return R.ok().putData("id", note.getId());
}
@DeleteMapping
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
applicationService.removeApplication(T.ListUtil.of(ids));
return R.ok();
}
@GetMapping("/{applicationId}/attachment")
public R queryAttachment(@PathVariable String applicationId) {
T.VerifyUtil.is(applicationId).notNull();
List<ApplicationAttachmentEntity> list = attachmentService.list(new LambdaQueryWrapper<ApplicationAttachmentEntity>().eq(ApplicationAttachmentEntity::getApplicationId, applicationId));
return R.ok().putData("records", list);
}
@PostMapping("/{applicationId}/attachment")
public R uploadAttachment(@PathVariable String applicationId, @RequestParam("files") List<MultipartFile> fileList) {
List<ApplicationAttachmentEntity> recordList = T.ListUtil.list(true);
for (int i = 0; i < fileList.size(); i++) {
MultipartFile file = fileList.get(i);
ApplicationAttachmentEntity attachmentEntity = attachmentService.saveAttachment(file.getResource(), applicationId);
recordList.add(attachmentEntity);
}
return R.ok().putData("records", recordList);
}
@DeleteMapping("/{applicationId}/attachment")
public R removedAttachment(@PathVariable String applicationId, @RequestParam String ids) {
attachmentService.removedAttachment(applicationId, ids);
return R.ok();
}
@GetMapping("/{applicationId}/signature")
public R querySignature(@PathVariable String applicationId) {
T.VerifyUtil.is(applicationId).notNull();
List<ApplicationSignatureEntity> signatureList = signatureService.queryList(applicationId);
return R.ok().putData("records", signatureList);
}
@GetMapping("/explore")
public R explore(@RequestParam String workspaceId, @RequestParam String pcapIds) {
String discoverUrl = applicationService.generateKibanaDiscoverUrl(workspaceId, pcapIds);
return R.ok().putData("url", discoverUrl);
}
@GetMapping("/{applicationId}/signature/{oldVersion}/{newVersion}")
public R signatureCompare(@PathVariable("applicationId") String applicationId,
@PathVariable("oldVersion") String oldVersion,
@PathVariable("newVersion") String newVersion) {
List<ApplicationSignatureEntity> list = signatureService.compare(applicationId, oldVersion, newVersion);
return R.ok().putData("records", list);
}
@PutMapping("/{applicationId}/signature/{version}/restore")
public R restore(@PathVariable("applicationId") String applicationId,
@PathVariable("version") String version) {
signatureService.restore(applicationId, version);
return R.ok();
}
}

View File

@@ -0,0 +1,65 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.app.service.IPackageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/package")
public class PackageController {
@Autowired
private IPackageService packageService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
PackageEntity entity = packageService.getById(id);
return R.ok().putData("record", entity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = packageService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody PackageEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(entity.getDescription()).notEmpty(RCode.PACKAGE_DESCRIPTION_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
PackageEntity pkgEntity = packageService.savePackage(entity);
return R.ok().putData("id", pkgEntity.getId());
}
@PutMapping
public R update(@RequestBody PackageEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getName()).notEmpty(RCode.NAME_CANNOT_EMPTY)
.and(entity.getDescription()).notEmpty(RCode.PACKAGE_DESCRIPTION_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
PackageEntity pkgEntity = packageService.updatePackage(entity);
return R.ok().putData("id", pkgEntity.getId());
}
@DeleteMapping
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
packageService.removePackage(T.ListUtil.of(ids));
return R.ok();
}
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApplicationAttachmentDao extends BaseMapper<ApplicationAttachmentEntity>{
}

View File

@@ -0,0 +1,26 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
@Mapper
public interface ApplicationDao extends BaseMapper<ApplicationEntity>{
List<ApplicationEntity> queryList(Page page, Map<String, Object> params);
@Select("select * from ( select * from application union select * from application_log ) app where app.id = #{id} and app.op_version = #{version}")
ApplicationEntity queryByApplicationAndLog(String id, String version);
List<ApplicationEntity> queryLogList(String id);
List<ApplicationEntity> compare(@Param("params") Map<String, Object> params);
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationLogEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApplicationLogDao extends BaseMapper<ApplicationLogEntity> {
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApplicationNoteDao extends BaseMapper<ApplicationNoteEntity>{
}

View File

@@ -0,0 +1,15 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface ApplicationSignatureDao extends BaseMapper<ApplicationSignatureEntity>{
List<ApplicationSignatureEntity> queryList(@Param("params") Map<Object, Object> params);
}

View File

@@ -0,0 +1,16 @@
package net.geedge.asw.module.app.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.app.entity.PackageEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface PackageDao extends BaseMapper<PackageEntity>{
List<PackageEntity> queryList(Page page, Map<String, Object> params);
}

View File

@@ -0,0 +1,25 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("application_attachment")
public class ApplicationAttachmentEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String applicationId;
private String name;
private String path;
private Long createTimestamp;
private String createUserId;
}

View File

@@ -0,0 +1,59 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import java.util.List;
@Data
@TableName("application")
public class ApplicationEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String tags;
private String packageName;
private String website;
private String provider;
private String status;
private String description;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
private Integer opVersion;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(exist = false)
private ApplicationSignatureEntity signature;
@TableField(exist = false)
private ApplicationNoteEntity note;
@TableField(exist = false)
private List<ApplicationAttachmentEntity> attatchments;
}

View File

@@ -0,0 +1,60 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import java.util.List;
@Data
@TableName("application_log")
public class ApplicationLogEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String tags;
private String packageName;
private String website;
private String provider;
private String status;
private String description;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
private Integer opVersion;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(exist = false)
private ApplicationSignatureEntity signature;
@TableField(exist = false)
private ApplicationNoteEntity note;
@TableField(exist = false)
private List<ApplicationAttachmentEntity> attatchments;
}

View File

@@ -0,0 +1,25 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("application_note")
public class ApplicationNoteEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String applicationId;
private String content;
private Long createTimestamp;
private String createUserId;
private Long opVersion;
}

View File

@@ -0,0 +1,30 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysUserEntity;
@Data
@TableName("application_signature")
public class ApplicationSignatureEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String applicationId;
private String content;
private Long createTimestamp;
private String createUserId;
private Long opVersion;
@TableField(exist = false)
private SysUserEntity createUser;
}

View File

@@ -0,0 +1,32 @@
package net.geedge.asw.module.app.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("package")
public class PackageEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String logo;
private String description;
private String platform;
private String version;
private String identifier;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
@TableField(exist = false)
private String workbookId;
}

View File

@@ -0,0 +1,12 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import org.springframework.core.io.Resource;
public interface ApplicationAttachmentService extends IService<ApplicationAttachmentEntity>{
ApplicationAttachmentEntity saveAttachment(Resource fileResource, String applicationId);
void removedAttachment(String applicationId, String ids);
}

View File

@@ -0,0 +1,9 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
public interface ApplicationNoteService extends IService<ApplicationNoteEntity>{
void saveNote(ApplicationNoteEntity note, String applicationId);
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import java.util.List;
public interface ApplicationSignatureService extends IService<ApplicationSignatureEntity>{
void saveSignature(ApplicationSignatureEntity signature, String applicationId);
List<ApplicationSignatureEntity> queryList(String applicationId);
List<ApplicationSignatureEntity> compare(String applicationId, String oldVersion, String newVersion);
void restore(String id, String version);
}

View File

@@ -0,0 +1,7 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationLogEntity;
public interface IApplicationLogService extends IService<ApplicationLogEntity> {
}

View File

@@ -0,0 +1,25 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import java.util.List;
import java.util.Map;
public interface IApplicationService extends IService<ApplicationEntity>{
ApplicationEntity detail(String id, String workspaceId);
Page queryList(Map<String, Object> params);
ApplicationEntity saveApplication(ApplicationEntity entity);
ApplicationEntity updateApplication(ApplicationEntity entity);
ApplicationEntity updateBasic(ApplicationEntity entity);
void removeApplication(List<String> ids);
String generateKibanaDiscoverUrl(String workspaceId, String pcapIds);
}

View File

@@ -0,0 +1,19 @@
package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.app.entity.PackageEntity;
import java.util.List;
import java.util.Map;
public interface IPackageService extends IService<PackageEntity>{
Page queryList(Map<String, Object> params);
PackageEntity savePackage(PackageEntity entity);
PackageEntity updatePackage(PackageEntity entity);
void removePackage(List<String> ids);
}

View File

@@ -0,0 +1,82 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationAttachmentDao;
import net.geedge.asw.module.app.entity.ApplicationAttachmentEntity;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.service.ApplicationAttachmentService;
import net.geedge.asw.module.app.service.IApplicationService;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
@Service
public class ApplicationAttachmentServiceImpl extends ServiceImpl<ApplicationAttachmentDao, ApplicationAttachmentEntity> implements ApplicationAttachmentService {
private static final Log log = Log.get();
@Autowired
private IApplicationService applicationService;
@Override
public ApplicationAttachmentEntity saveAttachment(Resource fileResource, String applicationId) {
ApplicationEntity app = applicationService.getById(applicationId);
ApplicationAttachmentEntity entity = new ApplicationAttachmentEntity();
try {
entity.setName(fileResource.getFilename());
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setApplicationId(applicationId);
// path
File destination = T.FileUtil.file(T.WebPathUtil.getRootPath(), app.getId(), fileResource.getFilename());
FileUtils.copyInputStreamToFile(fileResource.getInputStream(), destination);
entity.setPath(destination.getPath());
// 根据文件 applicationId path 判断是否已上存在,存在则响应当前实体
ApplicationAttachmentEntity attachment = this.getOne(new LambdaQueryWrapper<ApplicationAttachmentEntity>()
.eq(ApplicationAttachmentEntity::getApplicationId, applicationId)
.eq(ApplicationAttachmentEntity::getPath, destination.getPath()));
if (T.ObjectUtil.isNotNull(attachment)) {
return attachment;
}
// save
this.save(entity);
} catch (IOException e) {
log.error(e, "[saveAttachment] [error] [applicationId: {}]", applicationId);
throw new ASWException(RCode.ERROR);
}
return entity;
}
@Override
public void removedAttachment(String applicationId, String ids) {
List<String> idList = Arrays.asList(ids.split(","));
for (String id : idList) {
ApplicationAttachmentEntity attachment = this.getOne(new LambdaQueryWrapper<ApplicationAttachmentEntity>()
.eq(ApplicationAttachmentEntity::getApplicationId, applicationId)
.eq(ApplicationAttachmentEntity::getId, id));
T.FileUtil.del(FileUtil.file(attachment.getPath()));
this.removeById(id);
}
}
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.app.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.module.app.dao.ApplicationLogDao;
import net.geedge.asw.module.app.entity.ApplicationLogEntity;
import net.geedge.asw.module.app.service.IApplicationLogService;
import org.springframework.stereotype.Service;
@Service
public class ApplicationLogServiceImpl extends ServiceImpl<ApplicationLogDao, ApplicationLogEntity> implements IApplicationLogService {
}

View File

@@ -0,0 +1,36 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationNoteDao;
import net.geedge.asw.module.app.entity.ApplicationNoteEntity;
import net.geedge.asw.module.app.service.ApplicationNoteService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class ApplicationNoteServiceImpl extends ServiceImpl<ApplicationNoteDao, ApplicationNoteEntity> implements ApplicationNoteService {
@Override
@Transactional(rollbackFor = Exception.class)
public void saveNote(ApplicationNoteEntity note, String applicationId) {
// query last note
ApplicationNoteEntity noteLast = this.getOne(new LambdaQueryWrapper<ApplicationNoteEntity>()
.eq(ApplicationNoteEntity::getApplicationId, applicationId)
.orderByDesc(ApplicationNoteEntity::getOpVersion)
.last("limit 1"));
if (T.ObjectUtil.isNotEmpty(noteLast)){
note.setOpVersion(noteLast.getOpVersion() + 1);
}
//save note
note.setApplicationId(applicationId);
note.setCreateTimestamp(System.currentTimeMillis());
note.setCreateUserId(StpUtil.getLoginIdAsString());
this.save(note);
}
}

View File

@@ -0,0 +1,328 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.log.Log;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationDao;
import net.geedge.asw.module.app.entity.*;
import net.geedge.asw.module.app.service.*;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.service.IPcapService;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class ApplicationServiceImpl extends ServiceImpl<ApplicationDao, ApplicationEntity> implements IApplicationService {
private static final Log log = Log.get();
@Value("${kibana.url:127.0.0.1:5601}")
private String kibanaUrl;
@Autowired
private IApplicationLogService applicationLogService;
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IPcapService pcapService;
@Autowired
private ISysUserService userService;
@Autowired
private ApplicationSignatureService signatureService;
@Autowired
private ApplicationNoteService noteService;
@Autowired
private ApplicationAttachmentService attachmentService;
@Resource
private KibanaClient kibanaClient;
@Override
public ApplicationEntity detail(String id, String workspaceId) {
ApplicationEntity app = this.getOne(new LambdaQueryWrapper<ApplicationEntity>()
.eq(ApplicationEntity::getId, id)
.eq(ApplicationEntity::getWorkspaceId, workspaceId));
ApplicationSignatureEntity signature = signatureService.getOne(new LambdaQueryWrapper<ApplicationSignatureEntity>()
.eq(ApplicationSignatureEntity::getApplicationId, app.getId())
.orderByDesc(ApplicationSignatureEntity::getOpVersion)
.last("limit 1"));
app.setSignature(signature);
ApplicationNoteEntity note = noteService.getOne(new LambdaQueryWrapper<ApplicationNoteEntity>()
.eq(ApplicationNoteEntity::getApplicationId, app.getId())
.orderByDesc(ApplicationNoteEntity::getOpVersion)
.last("limit 1"));
app.setNote(note);
List<ApplicationAttachmentEntity> attachmentEntityList = attachmentService.list(new LambdaQueryWrapper<ApplicationAttachmentEntity>()
.eq(ApplicationAttachmentEntity::getApplicationId, app.getId()));
app.setAttatchments(attachmentEntityList);
SysUserEntity createUser = userService.getById(app.getCreateUserId());
SysUserEntity updateUser = userService.getById(app.getUpdateUserId());
app.setCreateUser(createUser);
app.setUpdateUser(updateUser);
return app;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
List<ApplicationEntity> packageList = this.getBaseMapper().queryList(page, params);
page.setRecords(packageList);
return page;
}
private void validateParam(ApplicationEntity entity, boolean isUpdate) {
ApplicationEntity one = this.getOne(new LambdaQueryWrapper<ApplicationEntity>()
.eq(ApplicationEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(ApplicationEntity::getName, entity.getName()));
if (T.ObjectUtil.isNotNull(one) && !isUpdate || T.ObjectUtil.isNotNull(one) && isUpdate && !T.StrUtil.equals(entity.getId(), one.getId())) {
throw ASWException.builder().rcode(RCode.APP_DUPLICATE_RECORD).build();
}
// package name format
if (T.ObjectUtil.isNotEmpty(entity.getPackageName()) && !T.JSONUtil.isTypeJSON(entity.getPackageName())) {
throw ASWException.builder().rcode(RCode.APP_PACKAGE_NAME_FORMAT_ERROR).build();
} else if (T.ObjectUtil.isEmpty(entity.getPackageName())) {
entity.setPackageName("{}");
}
// tags name format
if (T.StrUtil.isNotEmpty(entity.getTags()) && !T.JSONUtil.isTypeJSON(entity.getTags())) {
throw ASWException.builder().rcode(RCode.APP_TAGS_FORMAT_ERROR).build();
}
// signature
if (T.ObjectUtil.isNotEmpty(entity.getSignature())) {
if (!T.StrUtil.isNotEmpty(entity.getSignature().getContent())){
throw ASWException.builder().rcode(RCode.APP_SIGNATURE_CONTENT_CANNOT_EMPTY).build();
}
if (!T.JSONUtil.isTypeJSON(entity.getSignature().getContent())){
throw ASWException.builder().rcode(RCode.APP_SIGNATURE_CONTENT_CANNOT_EMPTY).build();
}
}
// note
if (T.ObjectUtil.isNotEmpty(entity.getNote()) && !T.StrUtil.isNotEmpty(entity.getNote().getContent())) {
throw ASWException.builder().rcode(RCode.APP_NOTE_CONTENT_CANNOT_EMPTY).build();
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApplicationEntity saveApplication(ApplicationEntity entity) {
this.validateParam(entity, false);
// save
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
this.save(entity);
if (T.ObjectUtil.isNotEmpty(entity.getSignature())){
// save signature
signatureService.saveSignature(entity.getSignature(), entity.getId());
}
if (T.ObjectUtil.isNotEmpty(entity.getNote())){
//save note
noteService.saveNote(entity.getNote(), entity.getId());
}
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public ApplicationEntity updateApplication(ApplicationEntity entity) {
this.validateParam(entity, true);
ApplicationEntity one = this.getById(entity.getId());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
entity.setOpVersion(one.getOpVersion() + 1);
// update
this.updateById(entity);
// save log
this.saveApplicationToLog(one);
if (T.ObjectUtil.isNotEmpty(entity.getSignature())){
// save signature
signatureService.saveSignature(entity.getSignature(), entity.getId());
}
if (T.ObjectUtil.isNotEmpty(entity.getNote())){
//save note
noteService.saveNote(entity.getNote(), entity.getId());
}
return entity;
}
private void saveApplicationToLog(ApplicationEntity one) {
ApplicationLogEntity applicationLogEntity = T.BeanUtil.toBean(one, ApplicationLogEntity.class);
applicationLogEntity.setUpdateTimestamp(System.currentTimeMillis());
applicationLogEntity.setUpdateUserId(StpUtil.getLoginIdAsString());
applicationLogEntity.setCreateTimestamp(System.currentTimeMillis());
applicationLogEntity.setCreateUserId(StpUtil.getLoginIdAsString());
applicationLogService.save(applicationLogEntity);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeApplication(List<String> ids) {
// remove
this.removeBatchByIds(ids);
applicationLogService.removeBatchByIds(ids);
signatureService.remove(new LambdaQueryWrapper<ApplicationSignatureEntity>().in(ApplicationSignatureEntity::getApplicationId, ids));
noteService.remove(new LambdaQueryWrapper<ApplicationNoteEntity>().in(ApplicationNoteEntity::getApplicationId, ids));
attachmentService.remove(new LambdaQueryWrapper<ApplicationAttachmentEntity>().in(ApplicationAttachmentEntity::getApplicationId, ids));
}
/**
* 1. 根据 workspace_name 查询 index-pattern 是否存在
* 2. 不存在则创建索引
*
* 维护格式示例:
* {
* "type": "index-pattern",
* "id": "workspace_id",
* "attributes": {
* "title": "workspace-{workspace_name}-*"
* }
* }
* @param workspaceId
* @param pcapIds
* @return kibana discover url
*/
@Override
public String generateKibanaDiscoverUrl(String workspaceId, String pcapIds) {
// verify
WorkspaceEntity workspace = workspaceService.getById(workspaceId);
T.VerifyUtil.is(workspace).notNull(RCode.SYS_RECORD_NOT_FOUND);
List<String> pcapIdList = T.StrUtil.split(pcapIds, ",").stream().filter(s -> T.StrUtil.isNotEmpty(s)).collect(Collectors.toList());
List<PcapEntity> pcapList = pcapService.list(new LambdaQueryWrapper<PcapEntity>().in(PcapEntity::getId, pcapIdList));
T.VerifyUtil.is(pcapList).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
// index name
String indexName = String.format("workspace-%s-*", workspace.getName());
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
String token = tokenInfo.getTokenValue();
JSONObject index = kibanaClient.findIndexPattern(token, indexName);
JSONArray savedObjects = index.getJSONArray("saved_objects");
// check if index exists
boolean indexExists = savedObjects.stream()
.filter(obj -> {
JSONObject attributes = ((JSONObject) obj).getJSONObject("attributes");
if (T.ObjectUtil.isEmpty(attributes)) return false;
String title = attributes.getString("title");
return T.StrUtil.equals(indexName, title);
})
.findFirst()
.isPresent();
if (log.isDebugEnabled()) {
log.debug("[generateKibanaDiscoverUrl] [idnex-pattern: {}] [exists: {}]", indexName, indexExists);
}
// create index
if (T.BooleanUtil.negate(indexExists)) {
JSONObject attributes = new JSONObject();
attributes.put("title", indexName);
JSONObject body = new JSONObject();
body.put("attributes", attributes);
kibanaClient.saveIndexPattern(token, workspaceId, body);
}
// build url
String baseUrl = UrlBuilder.ofHttp(kibanaUrl)
.addPath("/app/data-explorer/discover")
.addQuery("jwt", token)
.toString();
// build query param
String param1 = String.format("_a=(discover:(columns:!(_source),isDirty:!f,sort:!()),metadata:(indexPattern:'%s',view:discover))", workspaceId);
String param2 = "_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))";
String filter = pcapList.stream()
.map(PcapEntity::getId)
.map(pcapId -> "\"" + pcapId + "\"")
.collect(Collectors.joining("|", "pcap.id: (", ")"));
String param3 = String.format("_q=(filters:!(),query:(language:lucene,query:'%s'))", filter);
String query = String.format("?%s&%s&%s", param1, param2, param3);
String kibanaDiscoverUrl = baseUrl + "#" + query;
if (log.isDebugEnabled()) {
log.debug("[generateKibanaDiscoverUrl] [url: {}]", kibanaDiscoverUrl);
}
return kibanaDiscoverUrl;
}
@Override
public ApplicationEntity updateBasic(ApplicationEntity entity) {
ApplicationEntity one = this.getById(entity.getId());
if (T.ObjectUtil.isNotNull(one) && !T.StrUtil.equals(entity.getId(), one.getId())) {
throw ASWException.builder().rcode(RCode.APP_DUPLICATE_RECORD).build();
}
// package name format
if (T.ObjectUtil.isNotEmpty(entity.getPackageName()) && !T.JSONUtil.isTypeJSON(entity.getPackageName())) {
throw ASWException.builder().rcode(RCode.APP_PACKAGE_NAME_FORMAT_ERROR).build();
} else if (T.ObjectUtil.isEmpty(entity.getPackageName())) {
entity.setPackageName("{}");
}
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
entity.setOpVersion(one.getOpVersion() + 1);
this.saveApplicationToLog(one);
this.updateById(entity);
return entity;
}
}

View File

@@ -0,0 +1,81 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.ApplicationSignatureDao;
import net.geedge.asw.module.app.entity.ApplicationSignatureEntity;
import net.geedge.asw.module.app.service.ApplicationSignatureService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@Service
public class ApplicationSignatureServiceImpl extends ServiceImpl<ApplicationSignatureDao, ApplicationSignatureEntity> implements ApplicationSignatureService {
@Override
@Transactional(rollbackFor = Exception.class)
public void saveSignature(ApplicationSignatureEntity signature, String applicationId) {
// query last note
ApplicationSignatureEntity signatureLast = this.getOne(new LambdaQueryWrapper<ApplicationSignatureEntity>()
.eq(ApplicationSignatureEntity::getApplicationId, applicationId)
.orderByDesc(ApplicationSignatureEntity::getOpVersion)
.last("limit 1"));
if (T.ObjectUtil.isNotEmpty(signatureLast)){
signature.setOpVersion(signatureLast.getOpVersion() + 1);
}
// save signature
signature.setApplicationId(applicationId);
signature.setCreateTimestamp(System.currentTimeMillis());
signature.setCreateUserId(StpUtil.getLoginIdAsString());
this.save(signature);
}
@Override
public List<ApplicationSignatureEntity> queryList(String applicationId) {
Map<Object, Object> params = T.MapUtil.builder().put("applicationId", applicationId).build();
List<ApplicationSignatureEntity> list = this.getBaseMapper().queryList(params);
return list;
}
@Override
public List<ApplicationSignatureEntity> compare(String applicationId, String oldVersion, String newVersion) {
List<String> versionList = Arrays.asList(oldVersion, newVersion);
Map<Object, Object> params = T.MapUtil.builder()
.put("applicationId", applicationId)
.put("versions", versionList)
.build();
List<ApplicationSignatureEntity> list = this.getBaseMapper().queryList(params);
return list;
}
@Override
public void restore(String applicationId, String version) {
ApplicationSignatureEntity signature = this.getOne(new LambdaQueryWrapper<ApplicationSignatureEntity>()
.eq(ApplicationSignatureEntity::getApplicationId, applicationId)
.eq(ApplicationSignatureEntity::getOpVersion, version));
ApplicationSignatureEntity lastSignature = this.getOne(new LambdaQueryWrapper<ApplicationSignatureEntity>()
.eq(ApplicationSignatureEntity::getApplicationId, applicationId)
.orderByDesc(ApplicationSignatureEntity::getOpVersion)
.last("limit 1"));
if (T.ObjectUtil.isEmpty(signature)) {
throw ASWException.builder().rcode(RCode.APP_SIGNATURE_NOT_EXIST).build();
}
// restore
signature.setId(null);
signature.setOpVersion(lastSignature.getOpVersion() + 1);
this.save(signature);
}
}

View File

@@ -0,0 +1,90 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.dao.PackageDao;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.app.service.IPackageService;
import net.geedge.asw.module.workbook.service.IWorkbookResourceService;
import net.geedge.asw.module.workbook.util.WorkbookConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
@Service
public class PackageServiceImpl extends ServiceImpl<PackageDao, PackageEntity> implements IPackageService {
@Autowired
private IWorkbookResourceService workbookResourceService;
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
List<PackageEntity> packageList = this.getBaseMapper().queryList(page, params);
page.setRecords(packageList);
return page;
}
@Override
@Transactional(rollbackFor = Exception.class)
public PackageEntity savePackage(PackageEntity entity) {
PackageEntity one = this.getOne(new LambdaQueryWrapper<PackageEntity>()
.eq(PackageEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(PackageEntity::getName, entity.getName()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// save
this.save(entity);
// workbook resource
workbookResourceService.saveResource(entity.getWorkbookId(), entity.getId(), WorkbookConstant.ResourceType.PACKAGE.getValue());
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public PackageEntity updatePackage(PackageEntity entity) {
PackageEntity one = this.getOne(new LambdaQueryWrapper<PackageEntity>()
.eq(PackageEntity::getWorkspaceId, entity.getWorkspaceId())
.eq(PackageEntity::getName, entity.getName())
.ne(PackageEntity::getId, entity.getId()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// update
this.updateById(entity);
// workbook resource
workbookResourceService.saveResource(entity.getWorkbookId(), entity.getId(), WorkbookConstant.ResourceType.PACKAGE.getValue());
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removePackage(List<String> ids) {
// remove
this.removeBatchByIds(ids);
// workbook resource
workbookResourceService.removeResource(ids, WorkbookConstant.ResourceType.PACKAGE.getValue());
}
}

View File

@@ -0,0 +1,79 @@
package net.geedge.asw.module.feign;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.log.Log;
import feign.Feign;
import feign.form.FormEncoder;
import net.geedge.asw.module.feign.client.GeoipClient;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.module.feign.client.WebSharkClient;
import net.geedge.asw.module.feign.client.ZeekClient;
import net.geedge.asw.module.feign.support.Fastjson2Decoder;
import net.geedge.asw.module.feign.support.Fastjson2Encoder;
import net.geedge.asw.module.feign.support.Http2Client;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FeignClientConfiguration {
private static final Log log = Log.get();
@Value("${zeek.url:127.0.0.1:8086}")
private String zeekUrl;
@Value("${geoip.url:127.0.0.1:8087}")
private String geoipUrl;
@Value("${kibana.url:127.0.0.1:5601}")
private String kibanaUrl;
@Value("${webShark.url:127.0.0.1:8085}")
private String websharkurl;
@Bean("zeekClient")
public ZeekClient zeekClient() {
String url = UrlBuilder.ofHttp(zeekUrl).toString();
log.info("[zeekClient] [url: {}]", url);
return Feign.builder()
.encoder(new FormEncoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(ZeekClient.class, url);
}
@Bean("geoipClient")
public GeoipClient geoipClient() {
String url = UrlBuilder.ofHttp(geoipUrl).toString();
log.info("[geoipClient] [url: {}]", url);
return Feign.builder()
.encoder(new Fastjson2Encoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(GeoipClient.class, url);
}
@Bean("kibanaClient")
public KibanaClient kibanaClient() {
String url = UrlBuilder.ofHttp(kibanaUrl).toString();
log.info("[kibanaClient] [url: {}]", url);
return Feign.builder()
.encoder(new Fastjson2Encoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(KibanaClient.class, url);
}
@Bean("webSharkClient")
public WebSharkClient webSharkClient() {
String url = UrlBuilder.ofHttp(websharkurl).toString();
log.info("[webSharkClient] [url: {}]", url);
return Feign.builder()
.encoder(new FormEncoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(WebSharkClient.class, url);
}
}

View File

@@ -0,0 +1,88 @@
package net.geedge.asw.module.feign;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.T;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.ssl.SSLContextBuilder;
import org.opensearch.client.RestClient;
import org.opensearch.client.RestClientBuilder;
import org.opensearch.client.json.jackson.JacksonJsonpMapper;
import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch.core.InfoResponse;
import org.opensearch.client.transport.OpenSearchTransport;
import org.opensearch.client.transport.rest_client.RestClientTransport;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
@Component
public class OpenSearchClientConfiguration {
private static final Log log = Log.get();
@Value("${opensearch.url:127.0.0.1:7200}")
private String hostAndPort;
@Value("${opensearch.username:admin}")
private String username;
@Value("${opensearch.password:G1egG2U4NrjHRzV}")
private String password;
@Bean("openSearchClient")
public OpenSearchClient openSearchClient() {
try {
if (T.StrUtil.hasEmpty(this.hostAndPort, this.username, this.password)) {
throw new IllegalArgumentException("OpenSearchClient init info cannot be empty.");
}
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
// Setup SSL context to trust all certificates
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial((chain, authType) -> true)
.build();
String[] split = this.hostAndPort.split(":");
String host = split[0];
Integer port = Integer.valueOf(split[1]);
SSLContext finalSslContext = sslContext;
RestClientBuilder builder = RestClient.builder(
new HttpHost(host, port, "https"))
.setHttpClientConfigCallback(httpAsyncClientBuilder -> {
httpAsyncClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
.setSSLContext(finalSslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.setDefaultIOReactorConfig(
IOReactorConfig.custom()
.setIoThreadCount(1)
.build()
);
return httpAsyncClientBuilder;
});
RestClient restClient = builder.build();
OpenSearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
OpenSearchClient client = new OpenSearchClient(transport);
InfoResponse info = client.info();
log.info("[openSearchClient] [url: {}] [{}]", this.hostAndPort, info.version().distribution() + ": " + info.version().number());
return client;
} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | IOException e) {
log.error("[openSearchClient] [error] [url: {}]", this.hostAndPort);
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,14 @@
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONArray;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name = "geoipClient")
public interface GeoipClient {
@RequestLine("GET /geoip?ips={ip}")
JSONArray geoip(@Param("ip") String ipAddress);
}

View File

@@ -0,0 +1,23 @@
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONObject;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
@FeignClient(name = "kibanaClient")
@Headers("Authorization: Bearer {token}")
public interface KibanaClient {
@RequestLine("GET /api/saved_objects/_find?fields=title&per_page=10000&type=index-pattern&search_fields=title&search={name}")
JSONObject findIndexPattern(@Param("token") String token, @Param("name") String name);
@Headers({
"Content-Type: application/json",
"osd-xsrf: true"
})
@RequestLine("POST /api/saved_objects/index-pattern/{id}")
JSONObject saveIndexPattern(@Param("token") String token, @Param("id") String id, JSONObject body);
}

View File

@@ -0,0 +1,18 @@
package net.geedge.asw.module.feign.client;
import cn.hutool.json.JSONObject;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.File;
import java.util.Map;
@FeignClient(name = "webSharkClient")
public interface WebSharkClient {
@RequestLine("POST /webshark/upload")
@Headers("Content-Type: multipart/form-data")
JSONObject upload(@Param("fileKey") File file);
}

View File

@@ -0,0 +1,18 @@
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONArray;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.File;
@FeignClient(name = "zeekClient")
public interface ZeekClient {
@RequestLine("POST /upload")
@Headers("Content-Type: multipart/form-data")
JSONArray parser(@Param("pcap") File file);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2024 The Feign Authors
*
* 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 net.geedge.asw.module.feign.support;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.Decoder;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import static feign.Util.ensureClosed;
/**
* @author changjin wei(魏昌进)
*/
public class Fastjson2Decoder implements Decoder {
private final JSONReader.Feature[] features;
public Fastjson2Decoder() {
this(new JSONReader.Feature[0]);
}
public Fastjson2Decoder(JSONReader.Feature[] features) {
this.features = features;
}
@Override
public Object decode(Response response, Type type) throws IOException, FeignException {
if (response.status() == 404 || response.status() == 204) return Util.emptyValueOf(type);
if (response.body() == null) return null;
Reader reader = response.body().asReader(response.charset());
try {
return JSON.parseObject(reader, type, features);
} catch (JSONException e) {
if (e.getCause() != null && e.getCause() instanceof IOException) {
throw IOException.class.cast(e.getCause());
}
throw e;
} finally {
ensureClosed(reader);
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2012-2024 The Feign Authors
*
* 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 net.geedge.asw.module.feign.support;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import feign.RequestTemplate;
import feign.Util;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import java.lang.reflect.Type;
/**
* @author changjin wei(魏昌进)
*/
public class Fastjson2Encoder implements Encoder {
private final JSONWriter.Feature[] features;
public Fastjson2Encoder() {
this(new JSONWriter.Feature[0]);
}
public Fastjson2Encoder(JSONWriter.Feature[] features) {
this.features = features;
}
@Override
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
template.body(JSON.toJSONBytes(object, features), Util.UTF_8);
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright 2012-2024 The Feign Authors
*
* 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 net.geedge.asw.module.feign.support;
import feign.*;
import feign.Request.Options;
import feign.Request.ProtocolVersion;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpRequest.Builder;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;
import static feign.Util.enumForName;
public class Http2Client implements Client, AsyncClient<Object> {
private final HttpClient client;
private final Map<Integer, SoftReference<HttpClient>> clients = new ConcurrentHashMap<>();
/**
* Creates the new Http2Client using following defaults:
* <ul>
* <li>Connect Timeout: 10 seconds, as {@link Request.Options#Options()} uses</li>
* <li>Follow all 3xx redirects</li>
* <li>HTTP 2</li>
* </ul>
*
* @see Request.Options#Options()
*/
public Http2Client() {
this(HttpClient.newBuilder()
.followRedirects(Redirect.ALWAYS)
.version(Version.HTTP_2)
.connectTimeout(Duration.ofMillis(10000))
.build());
}
public Http2Client(Options options) {
this(newClientBuilder(options)
.version(Version.HTTP_2)
.build());
}
public Http2Client(HttpClient client) {
this.client = Util.checkNotNull(client, "HttpClient must not be null");
}
@Override
public Response execute(Request request, Options options) throws IOException {
final HttpRequest httpRequest;
try {
httpRequest = newRequestBuilder(request, options)
.version(client.version())
.build();
} catch (URISyntaxException e) {
throw new IOException("Invalid uri " + request.url(), e);
}
HttpClient clientForRequest = getOrCreateClient(options);
HttpResponse<InputStream> httpResponse;
try {
httpResponse = clientForRequest.send(httpRequest, BodyHandlers.ofInputStream());
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException(e);
}
return toFeignResponse(request, httpResponse);
}
@Override
public CompletableFuture<Response> execute(Request request,
Options options,
Optional<Object> requestContext) {
HttpRequest httpRequest;
try {
httpRequest = newRequestBuilder(request, options).build();
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Invalid uri " + request.url(), e);
}
HttpClient clientForRequest = getOrCreateClient(options);
CompletableFuture<HttpResponse<InputStream>> future =
clientForRequest.sendAsync(httpRequest, HttpResponse.BodyHandlers.ofInputStream());
return future.thenApply(httpResponse -> toFeignResponse(request, httpResponse));
}
protected Response toFeignResponse(Request request, HttpResponse<InputStream> httpResponse) {
final OptionalLong length = httpResponse.headers().firstValueAsLong("Content-Length");
return Response.builder()
.protocolVersion(enumForName(ProtocolVersion.class, httpResponse.version()))
.body(httpResponse.body(), length.isPresent() ? (int) length.getAsLong() : null)
.reason(httpResponse.headers().firstValue("Reason-Phrase").orElse(null))
.request(request)
.status(httpResponse.statusCode())
.headers(castMapCollectType(httpResponse.headers().map()))
.build();
}
private HttpClient getOrCreateClient(Options options) {
if (doesClientConfigurationDiffer(options)) {
// create a new client from the existing one - but with connectTimeout and followRedirect
// settings from options
final int clientKey = createClientKey(options);
SoftReference<HttpClient> requestScopedSoftReference = clients.get(clientKey);
HttpClient requestScoped =
requestScopedSoftReference == null ? null : requestScopedSoftReference.get();
if (requestScoped == null) {
java.net.http.HttpClient.Builder builder = newClientBuilder(options)
.sslContext(client.sslContext())
.sslParameters(client.sslParameters())
.version(client.version());
client.authenticator().ifPresent(builder::authenticator);
client.cookieHandler().ifPresent(builder::cookieHandler);
client.executor().ifPresent(builder::executor);
client.proxy().ifPresent(builder::proxy);
requestScoped = builder.build();
clients.put(clientKey, new SoftReference<>(requestScoped));
}
return requestScoped;
}
return client;
}
private boolean doesClientConfigurationDiffer(Options options) {
if ((client.followRedirects() == Redirect.ALWAYS) != options.isFollowRedirects()) {
return true;
}
return client.connectTimeout()
.map(timeout -> timeout.toMillis() != options.connectTimeoutMillis())
.orElse(true);
}
/**
* Creates integer key that represents {@link Options} settings based on
* {@link Http2Client#doesClientConfigurationDiffer(Options)} method
*
* @param options value
* @return integer key
*/
public int createClientKey(feign.Request.Options options) {
int key = options.connectTimeoutMillis();
if (options.isFollowRedirects()) {
key |= 1 << 31; // connectTimeoutMillis always positive, so we can use first sign bit for
// isFollowRedirects flag
}
return key;
}
private static java.net.http.HttpClient.Builder newClientBuilder(Options options) {
return HttpClient
.newBuilder()
.followRedirects(options.isFollowRedirects() ? Redirect.ALWAYS : Redirect.NEVER)
.connectTimeout(Duration.ofMillis(options.connectTimeoutMillis()));
}
private Builder newRequestBuilder(Request request, Options options) throws URISyntaxException {
URI uri = new URI(request.url());
final BodyPublisher body;
final byte[] data = request.body();
if (data == null) {
body = BodyPublishers.noBody();
} else {
body = BodyPublishers.ofByteArray(data);
}
final Builder requestBuilder = HttpRequest.newBuilder()
.uri(uri)
.timeout(Duration.ofMillis(options.readTimeoutMillis()))
.version(client.version());
final Map<String, Collection<String>> headers = filterRestrictedHeaders(request.headers());
if (!headers.isEmpty()) {
requestBuilder.headers(asString(headers));
}
return requestBuilder.method(request.httpMethod().toString(), body);
}
/**
* There is a bunch o headers that the http2 client do not allow to be set.
*
* @see jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET
*/
private static final Set<String> DISALLOWED_HEADERS_SET;
static {
// A case insensitive TreeSet of strings.
final TreeSet<String> treeSet = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
treeSet.addAll(Set.of("connection", "content-length", "expect", "host", "upgrade"));
DISALLOWED_HEADERS_SET = Collections.unmodifiableSet(treeSet);
}
private Map<String, Collection<String>> filterRestrictedHeaders(Map<String, Collection<String>> headers) {
final Map<String, Collection<String>> filteredHeaders = headers.keySet()
.stream()
.filter(headerName -> !DISALLOWED_HEADERS_SET.contains(headerName))
.collect(Collectors.toMap(
Function.identity(),
headers::get));
filteredHeaders.computeIfAbsent("Accept", key -> List.of("*/*"));
return filteredHeaders;
}
private Map<String, Collection<String>> castMapCollectType(Map<String, List<String>> map) {
final Map<String, Collection<String>> result = new HashMap<>();
map.forEach((key, value) -> result.put(key, new HashSet<>(value)));
return result;
}
private String[] asString(Map<String, Collection<String>> headers) {
return headers.entrySet().stream()
.flatMap(entry -> entry.getValue()
.stream()
.map(value -> Arrays.asList(entry.getKey(), value))
.flatMap(List::stream))
.toArray(String[]::new);
}
}

View File

@@ -0,0 +1,67 @@
package net.geedge.asw.module.runner.controller;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.runner.entity.JobEntity;
import net.geedge.asw.module.runner.service.IJobService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/job")
public class JobController {
@Autowired
private IJobService jobService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
JobEntity jobEntity = jobService.queryInfo(id);
return R.ok().putData("record", jobEntity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = jobService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody JobEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getRunnerId()).notEmpty(RCode.RUNNER_ID_CANNOT_EMPTY)
.and(entity.getPackageId()).notEmpty(RCode.PACKAGE_ID_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
JobEntity jobEntity = jobService.saveJob(entity);
return R.ok().putData("id", jobEntity.getId());
}
@DeleteMapping
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
jobService.removeJob(T.ListUtil.of(ids));
return R.ok();
}
@PutMapping("/cancel")
public R cancel(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
// TODO 其他处理
// update state
jobService.update(new LambdaUpdateWrapper<JobEntity>()
.in(JobEntity::getId, ids)
.set(JobEntity::getStatus, "cancel")
);
return R.ok();
}
}

View File

@@ -0,0 +1,171 @@
package net.geedge.asw.module.runner.controller;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.json.JSONObject;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.config.SpringContextUtils;
import net.geedge.asw.common.util.*;
import net.geedge.asw.module.feign.client.WebSharkClient;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.service.IPcapService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.*;
@RestController
@RequestMapping("/api/v1/pcap")
public class PcapController {
private static final Log log = Log.get();
@Autowired
private IPcapService pcapService;
@Value("${webShark.url:127.0.0.1:8085}")
private String websharkurl;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
PcapEntity pcapEntity = pcapService.queryInfo(id);
return R.ok().putData("record", pcapEntity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = pcapService.queryList(params);
return R.ok(page);
}
@PostMapping
@Transactional(rollbackFor = Exception.class)
public R add(@RequestParam(value = "files", required = true) List<MultipartFile> fileList,
@RequestParam(value = "descriptions", required = false) List<String> descriptionList,
@RequestParam(required = false) String workbookId,
@RequestParam(required = false) String workspaceId) throws IOException {
T.VerifyUtil.is(workspaceId).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
List<Object> recordList = T.ListUtil.list(true);
for (int i = 0; i < fileList.size(); i++) {
MultipartFile file = fileList.get(i);
String description = T.StrUtil.emptyToDefault(T.CollUtil.get(descriptionList, i), "");
PcapEntity pcapEntity = pcapService.savePcap(file.getResource(), description, workbookId, workspaceId);
recordList.add(
T.MapUtil.builder()
.put("id", pcapEntity.getId())
.build()
);
}
return R.ok().putData("records", recordList);
}
@PutMapping
@Transactional(rollbackFor = Exception.class)
public R update(@RequestBody List<Map<String, String>> body) {
List<Object> recordList = T.ListUtil.list(true);
for (Map<String, String> map : body) {
String id = T.MapUtil.getStr(map, "id", "");
if (T.StrUtil.isEmpty(id)) {
continue;
}
String description = T.MapUtil.getStr(map, "description", "");
pcapService.update(new LambdaUpdateWrapper<PcapEntity>()
.eq(PcapEntity::getId, id)
.set(PcapEntity::getDescription, description)
);
recordList.add(
T.MapUtil.builder()
.put("id", id)
.build()
);
}
return R.ok().putData("records", recordList);
}
@DeleteMapping
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
pcapService.deletePcap(ids);
return R.ok();
}
@PutMapping("/parse2session")
public R parse2session(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
pcapService.parse2session(ids);
return R.ok();
}
@GetMapping("/download")
public void download(HttpServletResponse response, String ids) throws IOException {
T.VerifyUtil.is(ids).notEmpty();
List<String> pcapIdList = Arrays.asList(ids.split(","));
List<PcapEntity> pcapList = pcapService.listByIds(pcapIdList);
if (T.CollectionUtil.isNotEmpty(pcapList) && pcapList.size() == 1) {
PcapEntity first = pcapList.getFirst();
File pcapFile = T.FileUtil.file(first.getPath());
ResponseUtil.downloadFile(response, MediaType.APPLICATION_OCTET_STREAM_VALUE, first.getName(), T.FileUtil.readBytes(pcapFile));
}
if (pcapList.size() > 1) {
File zipFile = T.FileUtil.file(T.StrUtil.concat(true, Constants.TEMP_PATH, "/", "pcap-", DateUtil.format(DateUtil.date(), DatePattern.PURE_DATETIME_PATTERN) + ".zip"));
List<File> fileList = pcapList.stream().map(x -> T.FileUtil.file(x.getPath())).toList();
T.ZipUtil.zip(zipFile, false, fileList.toArray(new File[0]));
ResponseUtil.downloadFile(response, zipFile.getName(), T.FileUtil.readBytes(zipFile));
T.FileUtil.del(zipFile);
}
}
@GetMapping("/{id}/webshark")
public R webshark(@PathVariable String id) {
T.VerifyUtil.is(id).notEmpty();
HashMap<Object, Object> result = T.MapUtil.newHashMap();
PcapEntity pcap = pcapService.getById(id);
File pcapFile = T.FileUtil.file(pcap.getPath());
String uploadFileName = T.StrUtil.concat(true, id, ".", T.FileUtil.getSuffix(pcapFile));
File newFile = FileUtil.copy(pcapFile, FileUtil.file(Constants.TEMP_PATH, uploadFileName), false);
try {
WebSharkClient webSharkClient = (WebSharkClient) SpringContextUtils.getBean("webSharkClient");
JSONObject obj = webSharkClient.upload(newFile);
String baseUrl = UrlBuilder.ofHttp(websharkurl)
.addPath("/webshark")
.toString();
result.put("fileName", uploadFileName);
result.put("url", baseUrl);
}catch (Exception e){
log.error(e, "webshark upload pcap error, id: {}", pcap.getId());
throw new ASWException(RCode.PCAP_UPLOAD_WEB_SHARK_ERROR);
}finally {
FileUtil.del(newFile);
}
return R.ok(result);
}
@PutMapping("/unparse2session")
public R unparse2session(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
pcapService.unparse2session(ids);
return R.ok();
}
}

View File

@@ -0,0 +1,174 @@
package net.geedge.asw.module.runner.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.lang.Opt;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.runner.entity.JobEntity;
import net.geedge.asw.module.runner.entity.PlaybookEntity;
import net.geedge.asw.module.runner.entity.RunnerEntity;
import net.geedge.asw.module.runner.service.IJobService;
import net.geedge.asw.module.runner.service.IRunnerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/runner")
public class RunnerController {
private static final Log log = Log.get();
@Autowired
private IJobService jobService;
@Autowired
private IRunnerService runnerService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
RunnerEntity runnerEntity = runnerService.getById(id);
return R.ok().putData("record", runnerEntity);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
T.VerifyUtil.is(params).notNull()
.and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
Page page = runnerService.queryList(params);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody RunnerEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
RunnerEntity runner = runnerService.saveRunner(entity);
return R.ok().putData("record", runner);
}
@PutMapping
public R update(@RequestBody RunnerEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getWorkspaceId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
RunnerEntity runner = runnerService.updateRunner(entity);
return R.ok().putData("record", runner);
}
@DeleteMapping("/{id}")
public R delete(@PathVariable("id") String id) {
runnerService.removeById(id);
return R.ok();
}
@SaIgnore
@PostMapping("/register")
public void register(@RequestHeader("Authorization") String token, HttpServletResponse response) throws IOException {
RunnerEntity runner = runnerService.getOne(new LambdaUpdateWrapper<RunnerEntity>().eq(RunnerEntity::getToken, token));
String status = Opt.ofNullable(runner).map(RunnerEntity::getStatus).orElseGet(() -> null);
if (!T.StrUtil.equals("online", status)) {
log.warn("[register] [runner is offline] [token: {}]", token);
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Runner is offline");
}
}
@SaIgnore
@PostMapping("/heartbeat")
public void heartbeat(@RequestHeader("Authorization") String token, @RequestBody Map<String, Integer> platformMap,
HttpServletResponse response) throws IOException {
RunnerEntity runner = runnerService.getOne(new LambdaUpdateWrapper<RunnerEntity>().eq(RunnerEntity::getToken, token));
String status = Opt.ofNullable(runner).map(RunnerEntity::getStatus).orElseGet(() -> null);
if (!T.StrUtil.equals("online", status)) {
log.warn("[heartbeat] [runner is offline] [token: {}]", token);
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Runner is offline");
return;
}
// update last_heartbeat_timestamp
runnerService.update(new LambdaUpdateWrapper<RunnerEntity>()
.set(RunnerEntity::getLastHeartbeatTimestamp, System.currentTimeMillis())
.eq(RunnerEntity::getId, runner.getId()));
// findjob by platform
String platform = platformMap.entrySet().stream().filter(entry -> entry.getValue() > 0).findFirst().map(entry -> entry.getKey()).orElseGet(null);
JobEntity job = jobService.assignPendingJob(runner.getId(), platform);
if (T.ObjectUtil.isNotNull(job)) {
// package
PackageEntity pkg = job.getPkg();
Map<String, String> pkgInfo = T.MapUtil.builder("id", pkg.getId())
.put("platform", pkg.getPlatform())
.put("identifier", pkg.getIdentifier())
.put("version", pkg.getVersion())
.build();
// playbook
PlaybookEntity playbook = job.getPlaybook();
Map<String, String> pbInfo = T.MapUtil.builder("id", playbook.getId())
.put("name", playbook.getName())
.build();
// response job info
Map<Object, Object> responseData = T.MapUtil.builder()
.put("id", job.getId())
.put("pkg", pkgInfo)
.put("playbook", pbInfo)
.build();
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html; charset=UTF-8");
response.getWriter().write(T.JSONUtil.toJsonStr(responseData));
}
}
@SaIgnore
@PutMapping("/trace/{jobId}")
public void trace(@RequestHeader("Authorization") String token, @PathVariable String jobId, @RequestBody byte[] bytes,
HttpServletResponse response) throws IOException {
RunnerEntity runner = runnerService.getOne(new LambdaUpdateWrapper<RunnerEntity>().eq(RunnerEntity::getToken, token));
String status = Opt.ofNullable(runner).map(RunnerEntity::getStatus).orElseGet(() -> null);
if (!T.StrUtil.equals("online", status)) {
log.warn("[trace] [runner is offline] [token: {}]", token);
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Runner is offline");
return;
}
try {
// 追加到文件中
String content = T.StrUtil.str(bytes, T.CharsetUtil.CHARSET_UTF_8);
jobService.appendTraceLogStrToFile(jobId, content);
} catch (Exception e) {
log.error("[trace] [error] [job: {}]", jobId);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
@SaIgnore
@PutMapping("/jobResult/{jobId}")
public void jobResult(@RequestHeader("Authorization") String token, @PathVariable String jobId, @RequestParam String state,
@RequestParam(value = "file", required = false) MultipartFile pcapFile,
HttpServletResponse response) throws IOException {
RunnerEntity runner = runnerService.getOne(new LambdaUpdateWrapper<RunnerEntity>().eq(RunnerEntity::getToken, token));
String status = Opt.ofNullable(runner).map(RunnerEntity::getStatus).orElseGet(() -> null);
if (!T.StrUtil.equals("online", status)) {
log.warn("[trace] [runner is offline] [token: {}]", token);
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Runner is offline");
return;
}
// 更新任务状态
jobService.updateJobResult(jobId, state, pcapFile);
}
}

View File

@@ -0,0 +1,19 @@
package net.geedge.asw.module.runner.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import net.geedge.asw.module.runner.entity.JobEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
@Mapper
public interface JobDao extends BaseMapper<JobEntity>{
List<JobEntity> queryList(IPage page, Map<String, Object> params);
JobEntity getPendingJobByPlatform(@Param("platform") String platform);
}

View File

@@ -0,0 +1,16 @@
package net.geedge.asw.module.runner.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.runner.entity.PcapEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface PcapDao extends BaseMapper<PcapEntity>{
List<PcapEntity> queryList(Page page, Map<String, Object> params);
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.runner.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.runner.entity.PlaybookEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PlaybookDao extends BaseMapper<PlaybookEntity>{
}

View File

@@ -0,0 +1,16 @@
package net.geedge.asw.module.runner.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.runner.entity.RunnerEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface RunnerDao extends BaseMapper<RunnerEntity>{
List<RunnerEntity> queryList(Page page, Map<String, Object> params);
}

View File

@@ -0,0 +1,53 @@
package net.geedge.asw.module.runner.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.PackageEntity;
@Data
@TableName("job")
public class JobEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String playbookId;
private String packageId;
private String runnerId;
private String scheduleId;
private String signatureIds;
private String tags;
private Long startTimestamp;
private Long endTimestamp;
private String status;
private String pcapId;
private String logPath;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
@TableField(exist = false)
private String workbookId;
@TableField(exist = false)
private ApplicationEntity application;
@TableField(exist = false)
@JsonProperty(value = "package")
private PackageEntity pkg;
@TableField(exist = false)
private RunnerEntity runner;
@TableField(exist = false)
private PlaybookEntity playbook;
}

View File

@@ -0,0 +1,49 @@
package net.geedge.asw.module.runner.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
@Data
@TableName("pcap")
public class PcapEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String description;
private String path;
private Long size;
private String md5;
private String status;
private String summary;
private Long createTimestamp;
private String createUserId;
private String workspaceId;
@TableField(exist = false)
private WorkspaceEntity workspace;
@TableField(exist = false)
private String jobId;
@TableField(exist = false)
private ApplicationEntity application;
@TableField(exist = false)
@JsonProperty(value = "package")
private PackageEntity pkg;
@TableField(exist = false)
private RunnerEntity runner;
@TableField(exist = false)
private PlaybookEntity playbook;
}

View File

@@ -0,0 +1,27 @@
package net.geedge.asw.module.runner.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("playbook")
public class PlaybookEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String appId;
private String tags;
private String script;
private Long opVersion;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
}

View File

@@ -0,0 +1,30 @@
package net.geedge.asw.module.runner.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("runner")
public class RunnerEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String token;
private String tags;
private String supportPlatforms;
private Integer shareFlag;
private String description;
private String status;
private Long lastHeartbeatTimestamp;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
private String workspaceId;
}

View File

@@ -0,0 +1,27 @@
package net.geedge.asw.module.runner.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.runner.entity.JobEntity;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
public interface IJobService extends IService<JobEntity>{
JobEntity queryInfo(String id);
Page queryList(Map<String, Object> params);
JobEntity saveJob(JobEntity entity);
void removeJob(List<String> ids);
JobEntity assignPendingJob(String id, String platform);
void appendTraceLogStrToFile(String jobId, String content) throws RuntimeException;
void updateJobResult(String jobId, String state, MultipartFile pcapFile);
}

View File

@@ -0,0 +1,25 @@
package net.geedge.asw.module.runner.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.runner.entity.PcapEntity;
import org.springframework.core.io.Resource;
import java.util.Map;
public interface IPcapService extends IService<PcapEntity>{
PcapEntity queryInfo(String id);
Page queryList(Map<String, Object> params);
PcapEntity savePcap(String jobId, Resource fileResource);
PcapEntity savePcap(Resource fileResource,String... params);
void deletePcap(String... ids);
void parse2session(String... ids);
void unparse2session(String[] ids);
}

View File

@@ -0,0 +1,8 @@
package net.geedge.asw.module.runner.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.runner.entity.PlaybookEntity;
public interface IPlaybookService extends IService<PlaybookEntity>{
}

View File

@@ -0,0 +1,17 @@
package net.geedge.asw.module.runner.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.runner.entity.RunnerEntity;
import java.util.Map;
public interface IRunnerService extends IService<RunnerEntity>{
Page queryList(Map<String, Object> params);
RunnerEntity saveRunner(RunnerEntity entity);
RunnerEntity updateRunner(RunnerEntity entity);
}

View File

@@ -0,0 +1,184 @@
package net.geedge.asw.module.runner.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.app.service.IApplicationService;
import net.geedge.asw.module.app.service.IPackageService;
import net.geedge.asw.module.runner.dao.JobDao;
import net.geedge.asw.module.runner.entity.JobEntity;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.entity.PlaybookEntity;
import net.geedge.asw.module.runner.entity.RunnerEntity;
import net.geedge.asw.module.runner.service.IJobService;
import net.geedge.asw.module.runner.service.IPcapService;
import net.geedge.asw.module.runner.service.IPlaybookService;
import net.geedge.asw.module.runner.service.IRunnerService;
import net.geedge.asw.module.runner.util.RunnerConstant;
import net.geedge.asw.module.workbook.service.IWorkbookResourceService;
import net.geedge.asw.module.workbook.util.WorkbookConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.util.List;
import java.util.Map;
@Service
public class JobServiceImpl extends ServiceImpl<JobDao, JobEntity> implements IJobService {
private static final Log log = Log.get();
@Autowired
private IPcapService pcapService;
@Autowired
private IRunnerService runnerService;
@Autowired
private IPlaybookService playbookService;
@Autowired
private IPackageService packageService;
@Autowired
private IApplicationService applicationService;
@Autowired
private IWorkbookResourceService workbookResourceService;
/**
* rootPath/result/{jobId}
*
* @param jobId
* @return
*/
private String getJobResultPath(String jobId) {
return T.FileUtil.file(T.WebPathUtil.getRootPath(), "result", jobId).getPath();
}
@Override
public JobEntity queryInfo(String id) {
JobEntity job = this.getById(id);
T.VerifyUtil.is(job).notNull(RCode.SYS_RECORD_NOT_FOUND);
RunnerEntity runner = runnerService.getById(job.getRunnerId());
job.setRunner(runner);
PlaybookEntity playbook = playbookService.getById(job.getPlaybookId());
job.setPlaybook(playbook);
PackageEntity pkg = packageService.getById(job.getPackageId());
job.setPkg(pkg);
if (T.ObjectUtil.isNotNull(playbook)) {
ApplicationEntity application = applicationService.getById(playbook.getAppId());
job.setApplication(application);
}
return job;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
List<JobEntity> jobList = this.getBaseMapper().queryList(page, params);
page.setRecords(jobList);
return page;
}
@Override
@Transactional(rollbackFor = Exception.class)
public JobEntity saveJob(JobEntity entity) {
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// save
this.save(entity);
// workbook resource
workbookResourceService.saveResource(entity.getWorkbookId(), entity.getId(), WorkbookConstant.ResourceType.JOB.getValue());
// trace log file path
File traceLogFile = T.FileUtil.file(this.getJobResultPath(entity.getId()), "trace.log");
this.update(new LambdaUpdateWrapper<JobEntity>()
.set(JobEntity::getLogPath, traceLogFile.getPath())
.eq(JobEntity::getId, entity.getId()));
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeJob(List<String> ids) {
// remove
this.removeBatchByIds(ids);
// workbook resource
workbookResourceService.removeResource(ids, WorkbookConstant.ResourceType.JOB.getValue());
}
@Override
public synchronized JobEntity assignPendingJob(String runnerId, String platform) {
if (T.StrUtil.hasEmpty(runnerId, platform)) {
return null;
}
// query
JobEntity job = this.getBaseMapper().getPendingJobByPlatform(platform);
if (T.ObjectUtil.isNotNull(job)) {
// update
this.update(new LambdaUpdateWrapper<JobEntity>()
.set(JobEntity::getRunnerId, runnerId)
.set(JobEntity::getStatus, RunnerConstant.JobStatus.RUNNING.getValue())
.set(JobEntity::getStartTimestamp, System.currentTimeMillis())
.eq(JobEntity::getId, job.getId())
);
}
return job;
}
@Override
public void appendTraceLogStrToFile(String jobId, String content) throws RuntimeException {
try {
JobEntity job = this.getById(jobId);
if (T.StrUtil.isEmpty(job.getLogPath())) {
File traceLogFile = T.FileUtil.file(this.getJobResultPath(jobId), "trace.log");
job.setLogPath(traceLogFile.getPath());
}
// append content
T.FileUtil.appendString(content, T.FileUtil.file(job.getLogPath()), T.CharsetUtil.CHARSET_UTF_8);
} catch (IORuntimeException e) {
log.error(e, "[appendTraceLogStrToFile] [error] [job: {}] [content: {}]", jobId, content);
throw new RuntimeException(e.getMessage());
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateJobResult(String jobId, String state, MultipartFile pcapFile) {
String pcapId = T.StrUtil.EMPTY;
// save pcap file
if (T.ObjectUtil.isNotNull(pcapFile)) {
PcapEntity pcapEntity = pcapService.savePcap(jobId, pcapFile.getResource());
pcapId = pcapEntity.getId();
}
// update job status&pcap_id
state = T.StrUtil.equals("success", state) ? RunnerConstant.JobStatus.PASSED.getValue() : state;
this.update(new LambdaUpdateWrapper<JobEntity>()
.set(JobEntity::getStatus, state)
.set(T.StrUtil.isNotEmpty(pcapId), JobEntity::getPcapId, pcapId)
.set(JobEntity::getEndTimestamp, System.currentTimeMillis())
.eq(JobEntity::getId, jobId)
);
}
}

View File

@@ -0,0 +1,287 @@
package net.geedge.asw.module.runner.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.SpringContextUtils;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.PackageEntity;
import net.geedge.asw.module.app.service.IApplicationService;
import net.geedge.asw.module.app.service.IPackageService;
import net.geedge.asw.module.runner.dao.PcapDao;
import net.geedge.asw.module.runner.entity.JobEntity;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.entity.PlaybookEntity;
import net.geedge.asw.module.runner.entity.RunnerEntity;
import net.geedge.asw.module.runner.service.IJobService;
import net.geedge.asw.module.runner.service.IPcapService;
import net.geedge.asw.module.runner.service.IPlaybookService;
import net.geedge.asw.module.runner.service.IRunnerService;
import net.geedge.asw.module.runner.util.PcapParserThread;
import net.geedge.asw.module.runner.util.RunnerConstant;
import net.geedge.asw.module.workbook.service.IWorkbookResourceService;
import net.geedge.asw.module.workbook.util.WorkbookConstant;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.apache.commons.io.FileUtils;
import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch.indices.DeleteIndexRequest;
import org.opensearch.client.opensearch.indices.ExistsRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
@Service
public class PcapServiceImpl extends ServiceImpl<PcapDao, PcapEntity> implements IPcapService {
private static final Log log = Log.get();
@Value("${sharkdApi.host:127.0.0.1}")
private String sharkdApiHostAddr;
@Autowired
private IJobService jobService;
@Autowired
private IRunnerService runnerService;
@Autowired
private IPlaybookService playbookService;
@Autowired
private IPackageService packageService;
@Autowired
private IApplicationService applicationService;
@Autowired
private IWorkbookResourceService workbookResourceService;
@Autowired
private IWorkspaceService workspaceService;
@Override
public PcapEntity queryInfo(String id) {
PcapEntity pcap = this.getById(id);
T.VerifyUtil.is(pcap).notNull(RCode.SYS_RECORD_NOT_FOUND);
JobEntity job = jobService.getOne(new LambdaQueryWrapper<JobEntity>().eq(JobEntity::getPcapId, pcap.getId()));
if (T.ObjectUtil.isNotNull(job)) {
pcap.setJobId(job.getId());
RunnerEntity runner = runnerService.getById(job.getRunnerId());
pcap.setRunner(runner);
PackageEntity pkg = packageService.getById(job.getPackageId());
pcap.setPkg(pkg);
PlaybookEntity playbook = playbookService.getById(job.getPlaybookId());
pcap.setPlaybook(playbook);
if (T.ObjectUtil.isNotNull(playbook)) {
ApplicationEntity application = applicationService.getById(playbook.getAppId());
pcap.setApplication(application);
}
}
return pcap;
}
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
List<PcapEntity> pcapList = this.getBaseMapper().queryList(page, params);
page.setRecords(pcapList);
return page;
}
@Override
public PcapEntity savePcap(String jobId, Resource fileResource) {
JobEntity job = jobService.getById(jobId);
return this.savePcap(fileResource, job.getTags(), job.getWorkbookId(), job.getWorkspaceId(), job.getCreateUserId());
}
@Override
public PcapEntity savePcap(Resource fileResource, String... params) {
String description = T.ArrayUtil.get(params, 0);
String workbookId = T.ArrayUtil.get(params, 1);
String workspaceId = T.ArrayUtil.get(params, 2);
String createUserId = T.StrUtil.emptyToDefault(T.ArrayUtil.get(params, 3), StpUtil.getLoginIdAsString());
PcapEntity entity = new PcapEntity();
try {
entity.setName(fileResource.getFilename());
entity.setDescription(description);
byte[] bytes = fileResource.getInputStream().readAllBytes();
entity.setSize((long) bytes.length);
entity.setStatus(RunnerConstant.PcapStatus.UPLOADED.getValue());
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(createUserId);
entity.setWorkspaceId(workspaceId);
// path
File destination = T.FileUtil.file(T.WebPathUtil.getRootPath(), workspaceId, fileResource.getFilename());
FileUtils.copyInputStreamToFile(fileResource.getInputStream(), destination);
entity.setPath(destination.getPath());
// md5
String md5Hex = T.DigestUtil.md5Hex(destination);
entity.setMd5(md5Hex);
// 根据文件 md5值 判断是否已上存在,存在则响应当前实体
PcapEntity findPcapByMd5 = this.getOne(new LambdaQueryWrapper<PcapEntity>().eq(PcapEntity::getMd5, md5Hex).eq(PcapEntity::getWorkspaceId, workspaceId));
if (T.ObjectUtil.isNotNull(findPcapByMd5)) {
return findPcapByMd5;
}
// save
this.save(entity);
// workbook resource
workbookResourceService.saveResource(workbookId, entity.getId(), WorkbookConstant.ResourceType.PCAP.getValue());
} catch (IOException e) {
log.error(e, "[savePcap] [error] [workspaceId: {}]", workspaceId);
throw new ASWException(RCode.ERROR);
}
return entity;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deletePcap(String... ids) {
for (String id : ids) {
PcapEntity pcap = this.getById(id);
// remove file
T.FileUtil.del(pcap.getPath());
// remove
this.removeById(id);
// update job pcap_id
jobService.update(new LambdaUpdateWrapper<JobEntity>()
.set(JobEntity::getPcapId, "")
.eq(JobEntity::getPcapId, id)
);
}
}
@Override
public void parse2session(String... ids) {
List<Runnable> taskList = T.ListUtil.list(true);
Long maxFileSize = 0L;
// parse thread config properties
Properties properties = new Properties();
properties.setProperty("sharkdApiHostAddr", this.sharkdApiHostAddr);
for (String id : ids) {
PcapEntity pcapEntity = this.getById(id);
if (T.ObjectUtil.isNotNull(pcapEntity)) {
WorkspaceEntity workspace = workspaceService.getById(pcapEntity.getWorkspaceId());
pcapEntity.setWorkspace(workspace);
PcapParserThread pcapParserThread = new PcapParserThread();
pcapParserThread.setPcapEntity(pcapEntity);
pcapParserThread.setProperties(properties);
taskList.add(pcapParserThread);
Long size = pcapEntity.getSize();
if (size > maxFileSize) {
maxFileSize = size;
}
}
}
if (T.CollUtil.isNotEmpty(taskList)) {
List<CompletableFuture<Void>> futures = taskList.stream()
.map(task -> CompletableFuture.runAsync(task))
.collect(Collectors.toList());
CompletableFuture<Void> allTasksFuture = CompletableFuture.allOf(
futures.toArray(new CompletableFuture[0])
);
try {
allTasksFuture.get(this.calculateParseThreadTimeout(maxFileSize), TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
log.error(e, "[parse2session] [error]");
throw new ASWException(RCode.ERROR);
}
}
}
@Override
public void unparse2session(String[] ids) {
OpenSearchClient openSearchClient = (OpenSearchClient) SpringContextUtils.getBean("openSearchClient");
for (String id : ids) {
PcapEntity pcapEntity = this.getById(id);
if (T.ObjectUtil.isNotNull(pcapEntity)) {
WorkspaceEntity workspace = workspaceService.getById(pcapEntity.getWorkspaceId());
pcapEntity.setWorkspace(workspace);
String pcapPath = pcapEntity.getPath();
String md5Hex = T.DigestUtil.md5Hex(T.FileUtil.file(pcapPath));
String workspaceName = pcapEntity.getWorkspace().getName();
String indexName = String.format("workspace-%s-%s", workspaceName, md5Hex);
try {
// check if index exists
boolean indexExists = openSearchClient.indices()
.exists(new ExistsRequest.Builder().index(indexName).build())
.value();
// if index exists, delete
if (indexExists) {
openSearchClient.indices().delete(new DeleteIndexRequest.Builder().index(indexName).build());
log.debug("delete openSearch index: {}", indexName);
}
}catch (Exception e){
log.error("delete openSearch index error index: {}", indexName);
throw new RuntimeException("delete openSearch index error ", e);
}
pcapEntity.setStatus(RunnerConstant.PcapStatus.UPLOADED.getValue());
this.updateById(pcapEntity);
}
}
}
/**
* calculate Parse Thread Timeout
*
* @param size
* @return
*/
private long calculateParseThreadTimeout(Long size) {
// 小于 1MB 的文件,超时时间为 1分钟
if (size <= 1048576) {
return 60;
// 小于10MB的文件超时时间为 3分钟
} else if (size <= 1048576 * 10) {
return 60 * 3;
// 其他,超时时间为 10分钟
} else {
return 60 * 10;
}
}
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.runner.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.module.runner.dao.PlaybookDao;
import net.geedge.asw.module.runner.entity.PlaybookEntity;
import net.geedge.asw.module.runner.service.IPlaybookService;
import org.springframework.stereotype.Service;
@Service
public class PlaybookServiceImpl extends ServiceImpl<PlaybookDao, PlaybookEntity> implements IPlaybookService {
}

View File

@@ -0,0 +1,51 @@
package net.geedge.asw.module.runner.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.runner.dao.RunnerDao;
import net.geedge.asw.module.runner.entity.RunnerEntity;
import net.geedge.asw.module.runner.service.IRunnerService;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
@Service
public class RunnerServiceImpl extends ServiceImpl<RunnerDao, RunnerEntity> implements IRunnerService {
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);
List<RunnerEntity> jobList = this.getBaseMapper().queryList(page, params);
page.setRecords(jobList);
return page;
}
@Override
public RunnerEntity saveRunner(RunnerEntity entity) {
entity.setCreateTimestamp(System.currentTimeMillis());
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setCreateUserId(StpUtil.getLoginIdAsString());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// token
entity.setToken(T.IdUtil.fastSimpleUUID());
// save
this.save(entity);
return entity;
}
@Override
public RunnerEntity updateRunner(RunnerEntity entity) {
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
// update
this.updateById(entity);
return entity;
}
}

View File

@@ -0,0 +1,269 @@
package net.geedge.asw.module.runner.util;
import cn.hutool.log.Log;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.Data;
import net.geedge.asw.common.config.SpringContextUtils;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.feign.client.GeoipClient;
import net.geedge.asw.module.feign.client.ZeekClient;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.service.IPcapService;
import org.apache.commons.lang3.time.StopWatch;
import org.opensearch.client.opensearch.OpenSearchClient;
import org.opensearch.client.opensearch.core.BulkRequest;
import org.opensearch.client.opensearch.core.BulkResponse;
import org.opensearch.client.opensearch.core.bulk.BulkResponseItem;
import org.opensearch.client.opensearch.indices.CreateIndexRequest;
import org.opensearch.client.opensearch.indices.DeleteIndexRequest;
import org.opensearch.client.opensearch.indices.ExistsRequest;
import org.opensearch.client.opensearch.indices.IndexSettings;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;
import static net.geedge.asw.module.runner.util.RunnerConstant.PcapStatus;
@Data
public class PcapParserThread implements Runnable {
private Log log = Log.get();
private Properties properties;
private PcapEntity pcapEntity;
private IPcapService pcapService;
private ZeekClient zeekClient;
private GeoipClient geoipClient;
private OpenSearchClient openSearchClient;
private void init() {
pcapService = SpringContextUtils.getBean(IPcapService.class);
zeekClient = (ZeekClient) SpringContextUtils.getBean("zeekClient");
geoipClient = (GeoipClient) SpringContextUtils.getBean("geoipClient");
openSearchClient = (OpenSearchClient) SpringContextUtils.getBean("openSearchClient");
}
@Override
public void run() {
Thread.currentThread().setName("pcap-parser-thread-" + pcapEntity.getId());
log.info("job pcap parser start");
if (log.isDebugEnabled()) {
log.debug("pcapInfo: {}", T.JSONUtil.toJsonStr(pcapEntity));
}
StopWatch sw = new StopWatch();
sw.start();
try {
// init
this.init();
// parsing
this.updateStatus(PcapStatus.PARSING.getValue());
// parser
this.parser();
// indexed
this.updateStatus(PcapStatus.INDEXED.getValue());
} catch (Exception e) {
// error
this.updateStatus(PcapStatus.ERROR.getValue());
log.error(e, "job pcap parser error, id: {}", pcapEntity.getId());
} finally {
sw.stop();
log.info("job pcap parser end. id: {} Run Time: {}", pcapEntity.getId(), sw.toString());
}
}
/**
* parser
*/
private void parser() {
// zeek
JSONArray jsonArray = zeekClient.parser(T.FileUtil.file(pcapEntity.getPath()));
if (log.isDebugEnabled()) {
log.debug("[parse] [zeek parse pcap file] [size: {}]", jsonArray.size());
}
// geoip
List<String> ipList = jsonArray.stream()
.map(obj -> T.MapUtil.getStr((JSONObject) obj, "id.resp_h", ""))
.filter(s -> T.StrUtil.isNotEmpty(s))
.distinct()
.collect(Collectors.toList());
Map<String, JSONObject> geoipInfo = this.queryGeoip(ipList);
// add custom field
String pcapId = pcapEntity.getId();
String pcapName = T.FileUtil.getName(pcapEntity.getPath());
Long tcpStream = 0L, udpStream = 0L;
String sharkdApiHostAddr = properties.getProperty("sharkdApiHostAddr", "127.0.0.1");
for (Object obj : jsonArray) {
JSONObject pojo = (JSONObject) obj;
pojo.put("pcap.id", pcapId);
pojo.put("pcap.name", pcapName);
String proto = T.MapUtil.getStr(pojo, "proto", "");
if (T.StrUtil.equalsIgnoreCase("tcp", proto)) {
Long streamId = tcpStream++;
pojo.put("pcap.tcp_stream", streamId);
pojo.put("pcap.stream_url", String.format("http://%s/pcap/%s/tcp/%s", sharkdApiHostAddr, pcapId, streamId));
}
if (T.StrUtil.equalsIgnoreCase("udp", proto)) {
Long streamId = udpStream++;
pojo.put("pcap.udp_stream", streamId);
pojo.put("pcap.stream_url", String.format("http://%s/pcap/%s/udp/%s", sharkdApiHostAddr, pcapId, streamId));
}
String resp = T.MapUtil.getStr(pojo, "id.resp_h", "");
if (T.StrUtil.isNotEmpty(resp)) {
JSONObject jsonObject = T.MapUtil.get(geoipInfo, resp, JSONObject.class, new JSONObject());
pojo.put("id.resp_country", T.MapUtil.getStr(jsonObject, "country", ""));
pojo.put("id.resp_asn", T.MapUtil.getStr(jsonObject, "asn", ""));
pojo.put("id.resp_asname", T.MapUtil.getStr(jsonObject, "asname", ""));
}
}
// summary
this.statisticSummary(jsonArray);
// opensearch
this.uploadToOpenSearch(jsonArray);
}
/**
* query geoip
*
* @param ipList
* @return
*/
private Map<String, JSONObject> queryGeoip(List<String> ipList) {
JSONArray result = new JSONArray();
int batchSize = 100;
for (int i = 0; i < ipList.size(); i += batchSize) {
List<String> currentBatch = ipList.subList(i, Math.min(i + batchSize, ipList.size()));
String queryParam = currentBatch.stream().collect(Collectors.joining(","));
JSONArray array = geoipClient.geoip(queryParam);
result.addAll(array);
}
Map<String, JSONObject> map = result.stream().collect(
Collectors.toMap(
obj -> T.MapUtil.getStr((JSONObject) obj, "ip"),
obj -> (JSONObject) obj
));
return map;
}
/**
* statistic summary
*
* @param jsonArray
*/
private void statisticSummary(JSONArray jsonArray) {
if (T.ObjectUtil.isEmpty(jsonArray)) {
log.warn("[statisticSummary] [data array is empty] [id: {}]", pcapEntity.getId());
} else {
Set<String> services = new HashSet<>();
Long packets = 0L;
for (Object obj : jsonArray) {
JSONObject pojo = (JSONObject) obj;
long origPkts = pojo.getLongValue("orig_pkts", 0);
long respPkts = pojo.getLongValue("resp_pkts", 0);
packets = packets + (origPkts + respPkts);
services.add(pojo.getString("proto"));
services.add(pojo.getString("service"));
}
JSONObject first = (JSONObject) jsonArray.getFirst();
JSONObject last = (JSONObject) jsonArray.getLast();
Map<Object, Object> m = T.MapUtil.builder()
.put("startTimestamp", first.getBigDecimal("ts"))
.put("endTimestamp", last.getBigDecimal("ts"))
.put("sessions", jsonArray.size())
.put("packets", packets)
.put("services", services)
.build();
pcapService.update(new LambdaUpdateWrapper<PcapEntity>()
.set(PcapEntity::getSummary, T.JSONUtil.toJsonStr(m))
.eq(PcapEntity::getId, pcapEntity.getId())
);
}
}
/**
* upload to opensearch
*
* @param jsonArray
*/
private void uploadToOpenSearch(JSONArray jsonArray) {
String pcapPath = pcapEntity.getPath();
String md5Hex = T.DigestUtil.md5Hex(T.FileUtil.file(pcapPath));
String workspaceName = pcapEntity.getWorkspace().getName();
String indexName = String.format("workspace-%s-%s", workspaceName, md5Hex);
try {
// check if index exists
boolean indexExists = openSearchClient.indices()
.exists(new ExistsRequest.Builder().index(indexName).build())
.value();
if (log.isDebugEnabled()) {
log.debug("[uploadToOpenSearch] [index: {}] [exists: {}]", indexName, indexExists);
}
// if index exists, delete
if (indexExists) {
openSearchClient.indices().delete(new DeleteIndexRequest.Builder().index(indexName).build());
log.debug("[uploadToOpenSearch] [index: {}] [deleted]", indexName);
}
// create index with default settings
openSearchClient.indices().create(
new CreateIndexRequest.Builder()
.index(indexName)
.settings(new IndexSettings.Builder().build())
.build()
);
// upload data in bulk
BulkRequest.Builder br = new BulkRequest.Builder();
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject jsonObject = (JSONObject) jsonArray.get(i);
String id = String.valueOf(i);
br.operations(op -> op.index(
idx -> idx.index(indexName)
.id(id)
.document(jsonObject)
));
}
BulkResponse result = openSearchClient.bulk(br.build());
// log errors, if any
if (result.errors()) {
log.error("[uploadToOpenSearch] [bulk had errors]");
for (BulkResponseItem item : result.items()) {
if (item.error() != null) {
log.error("[uploadToOpenSearch] [error reason]", item.error().reason());
}
}
}
} catch (IOException e) {
log.error("[uploadToOpenSearch] [error] [index: {}]", indexName);
throw new RuntimeException("Failed to upload data to OpenSearch", e);
}
}
/**
* update pcap status
*
* @param status
*/
private void updateStatus(String status) {
pcapService.update(new LambdaUpdateWrapper<PcapEntity>()
.set(PcapEntity::getStatus, status)
.eq(PcapEntity::getId, pcapEntity.getId())
);
}
}

View File

@@ -0,0 +1,56 @@
package net.geedge.asw.module.runner.util;
public class RunnerConstant {
/**
* job status
*/
public enum JobStatus {
CREATED("created"),
PENDING("pending"),
RUNNING("running"),
PASSED("passed"),
FAILED("failed"),
CANCEL("cancel");
private String value;
JobStatus(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
/**
* pcap status
*/
public enum PcapStatus {
UPLOADED("Uploaded"),
PARSING("Parsing"),
INDEXED("Indexed"),
ERROR("Error");
private String value;
PcapStatus(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
}

View File

@@ -0,0 +1,180 @@
package net.geedge.asw.module.runner.util;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONConfig;
import cn.hutool.log.Log;
import lombok.Data;
import lombok.NoArgsConstructor;
import net.geedge.asw.common.config.SpringContextUtils;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.T;
import org.apache.commons.lang3.time.StopWatch;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
@Data
@Deprecated
@NoArgsConstructor
public class SignatureExtract {
private static final Log log = Log.get();
private String id;
private String path;
private String tsharkPath = "/usr/bin/tshark";
public SignatureExtract(String id, String path) {
this.id = id;
this.path = path;
}
/**
* signature
*
* @return
*/
public String signature() {
log.info("[signature] [begin] [id: {}] [path: {}]", id, path);
StopWatch sw = new StopWatch();
sw.start();
try {
this.tsharkPath = SpringContextUtils.getProperty("tshark.path", "/usr/bin/tshark");
log.info("[signature] [tshark path: {}]", this.tsharkPath);
SignatureUtil signatureObject = new SignatureUtil(this.tsharkPath);
signatureObject.getStreamSignatureFromTshrak(path);
List<Map<String, String>> allFrameSignatureDictList = signatureObject.getOutputDictList();
// Get basic information of TCP data streams
List<Map<String, Object>> tcpStreamBasicInfoList = SignatureUtil.getTCPStreamBaseInfo(allFrameSignatureDictList);
List<Map<String, Object>> tcpStreamAllInfoList = T.ListUtil.list(false, tcpStreamBasicInfoList);
// Get other information of TCP data streams
// Processing data stream by stream
for (int i = 0; i < tcpStreamAllInfoList.size(); i++) {
String streamID = T.MapUtil.getStr(tcpStreamAllInfoList.get(i), "StreamID");
// Get all the Frame IDs of the data stream
List<Map<String, String>> tcpFrameSignatureList = signatureObject.getOneTcpFrameSignatureList(streamID);
// Merge signature information from all Frame IDs
// TCP data flow analysis
this.tcpDataFlowAnalysis(signatureObject, tcpStreamAllInfoList.get(i), tcpFrameSignatureList);
// General data flow analysis (common, ip, dns, http, ssl)
this.generalDataFlowAnalysis(signatureObject, tcpStreamAllInfoList.get(i), tcpFrameSignatureList);
}
// Get basic information of UDP data streams
List<Map<String, Object>> udpStreamBaseInfo = SignatureUtil.getUDPStreamBaseInfo(allFrameSignatureDictList);
List<Map<String, Object>> udpStreamAllInfoList = T.ListUtil.list(false, udpStreamBaseInfo);
// Get other information of UDP data streams
// Processing data stream by stream
for (int i = 0; i < udpStreamAllInfoList.size(); i++) {
String streamID = T.MapUtil.getStr(udpStreamAllInfoList.get(i), "StreamID");
// Get all the Frame IDs of the data stream
List<Map<String, String>> udpFrameSignatureList = signatureObject.getOneUdpFrameSignatureList(streamID);
// Merge signature information from all Frame IDs
// UDP data flow analysis
this.udpDataFlowAnalysis(signatureObject, udpStreamAllInfoList.get(i), udpFrameSignatureList);
// General data flow analysis (common, ip, dns, http, ssl)
this.generalDataFlowAnalysis(signatureObject, udpStreamAllInfoList.get(i), udpFrameSignatureList);
}
// result
List<Object> resultOutputDict = T.ListUtil.list(true);
resultOutputDict.addAll(tcpStreamAllInfoList);
resultOutputDict.addAll(udpStreamAllInfoList);
JSONConfig jsonConfig = new JSONConfig();
jsonConfig.setKeyComparator(Comparator.comparing(String::toString));
JSONArray jsonArray = new JSONArray(resultOutputDict, jsonConfig);
return jsonArray.toJSONString(0);
} catch (Exception e) {
log.error(e, "[signature] [error] [id: {}] [path: {}]", id, path);
throw new ASWException("pcap file parse error. pcap id: " + id);
} finally {
sw.stop();
log.info("[signature] [finshed] [id: {}] [Run Time: {}]", id, sw.toString());
}
}
/**
* data field
*
* @param signatureObject
* @param streamDict
* @param frameSignatureList
*/
private void generalDataFlowAnalysis(SignatureUtil signatureObject, Map<String, Object> streamDict, List<Map<String, String>> frameSignatureList) {
// common
streamDict.put("common.server_fqdn", signatureObject.ssl_extensions_server_name(frameSignatureList));
streamDict.put("common.app_id", new String[]{"unknow"});
if (T.MapUtil.getStr(frameSignatureList.get(0), "ip.proto").equals("6")) {
streamDict.put("srcport", signatureObject.tcp_srcport(frameSignatureList));
streamDict.put("dstport", signatureObject.tcp_dstport(frameSignatureList));
} else {
streamDict.put("srcport", signatureObject.udp_srcport(frameSignatureList));
streamDict.put("dstport", signatureObject.udp_dstport(frameSignatureList));
}
// ip
streamDict.put("ip.src", signatureObject.ip_src(frameSignatureList));
streamDict.put("ip.dst", signatureObject.ip_dst(frameSignatureList));
streamDict.put("ip.proto", signatureObject.ip_proto(frameSignatureList));
streamDict.put("heartbeat_flag", signatureObject.heartbeat_flag(frameSignatureList));
// dns
streamDict.put("dns.qry.name", signatureObject.dns_qry_name(frameSignatureList));
// http
streamDict.put("http.request.full_uri", signatureObject.http_request_full_uri(frameSignatureList));
streamDict.put("http.request.header", signatureObject.http_request_header(frameSignatureList));
streamDict.put("http.response.header", signatureObject.http_response_header(frameSignatureList));
// ssl
streamDict.put("ssl.handshake.certificate.algorithm_identifier", signatureObject.ssl_algorithm_identifier(frameSignatureList));
streamDict.put("ssl.handshake.certificate.serial_number", signatureObject.ssl_serial_number(frameSignatureList));
streamDict.put("ssl.handshake.certificate.issuer_common_name", signatureObject.ssl_issuer_common_name(frameSignatureList));
streamDict.put("ssl.handshake.certificate.issuer_organization_name", signatureObject.ssl_issuer_organization_name(frameSignatureList));
streamDict.put("ssl.handshake.certificate.issuer_country_name", signatureObject.ssl_issuer_country_name(frameSignatureList));
streamDict.put("ssl.handshake.certificate.subject_common_name", signatureObject.ssl_subject_common_name(frameSignatureList));
streamDict.put("ssl.handshake.certificate.subject_organization_name", signatureObject.ssl_subject_organization_name(frameSignatureList));
streamDict.put("ssl.handshake.certificate.subject_country_name", signatureObject.ssl_subject_country_name(frameSignatureList));
streamDict.put("ssl.handshake.certificate.not_valid_before", signatureObject.ssl_not_valid_before(frameSignatureList));
streamDict.put("ssl.handshake.certificate.not_valid_after", signatureObject.ssl_not_valid_after(frameSignatureList));
streamDict.put("ssl.handshake.certificate.algorithm_id", signatureObject.ssl_algorithm_id(frameSignatureList));
streamDict.put("ssl.analysis.ja3", signatureObject.ssl_ja3(frameSignatureList));
streamDict.put("ssl.analysis.sni_absent", signatureObject.ssl_sni_absent(frameSignatureList));
streamDict.put("ssl.analysis.ech_enabled", signatureObject.ssl_ech_enabled(frameSignatureList));
streamDict.put("ssl.analysis.esni_enabled", signatureObject.ssl_analysis_esni_enabled(frameSignatureList));
}
/**
* udp
*
* @param signatureObject
* @param streamDict
* @param frameSignatureList
*/
private void udpDataFlowAnalysis(SignatureUtil signatureObject, Map<String, Object> streamDict, List<Map<String, String>> frameSignatureList) {
streamDict.put("udp.payload.c2s_first_data", signatureObject.udp_c2s_first_data(frameSignatureList));
streamDict.put("udp.payload.s2c_first_data", signatureObject.udp_s2c_first_data(frameSignatureList));
streamDict.put("udp.payload.c2s_first_data_len", signatureObject.udp_c2s_first_data_len(frameSignatureList));
streamDict.put("udp.payload.s2c_first_data_len", signatureObject.udp_s2c_first_data_len(frameSignatureList));
streamDict.put("udp.payload", signatureObject.udp_get_payload(frameSignatureList));
}
/**
* tcp
*
* @param signatureObject
* @param streamDict
* @param frameSignatureList
*/
private void tcpDataFlowAnalysis(SignatureUtil signatureObject, Map<String, Object> streamDict, List<Map<String, String>> frameSignatureList) {
streamDict.put("tcp.payload.c2s_first_data", signatureObject.tcp_c2s_first_data(frameSignatureList));
streamDict.put("tcp.payload.s2c_first_data", signatureObject.tcp_s2c_first_data(frameSignatureList));
streamDict.put("tcp.payload.c2s_first_data_len", signatureObject.tcp_c2s_first_data_len(frameSignatureList));
streamDict.put("tcp.payload.s2c_first_data_len", signatureObject.tcp_s2c_first_data_len(frameSignatureList));
streamDict.put("tcp.payload", signatureObject.tcp_get_payload(frameSignatureList));
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,29 +1,33 @@
package net.geedge.asw.module.sys.controller; package net.geedge.asw.module.sys.controller;
import java.util.Map; import cn.dev33.satoken.config.SaTokenConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import cn.hutool.jwt.signers.JWTSignerUtil;
import net.geedge.asw.common.util.R; import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T; import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysAuthService; import net.geedge.asw.module.sys.service.ISysAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController @RestController
@RequestMapping("/sys") @RequestMapping("/api/v1")
public class SysAuthController { public class SysAuthController {
@Autowired @Autowired
private ISysAuthService authService; private ISysAuthService authService;
@Autowired
private SaTokenConfig saTokenConfig;
record AuthRecord(String userName, String pwd) {} record AuthRecord(String userName, String pwd) {}
@PostMapping("/login") @PostMapping("/login")
@@ -32,6 +36,30 @@ public class SysAuthController {
.notEmpty(RCode.SYS_USER_PWD_ERROR); .notEmpty(RCode.SYS_USER_PWD_ERROR);
SysUserEntity userEntity = authService.login(record.userName(), record.pwd()); SysUserEntity userEntity = authService.login(record.userName(), record.pwd());
SaTokenInfo tokenInfo = StpUtil.getTokenInfo(); SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
String tokenValue = tokenInfo.getTokenValue();
JWT jwt = JWTUtil.parseToken(tokenValue);
// payload
jwt.setPayload("sub", userEntity.getUserName());
Map<String, Object> permissions = authService.userPermissions();
String roles = ((List<String>) T.JSONUtil.getByPath(T.JSONUtil.parse(permissions), "records.role.name")).stream()
.distinct()
.collect(Collectors.joining(","));
jwt.setPayload("roles", roles);
Long eff = Long.valueOf(jwt.getPayload("eff").toString());
jwt.setPayload("exp", eff == -1L ? -1 : (eff / 1000));
jwt.setPayload("iat", System.currentTimeMillis() / 1000);
jwt.setPayload("nbf", System.currentTimeMillis() / 1000);
String sign = jwt.sign(JWTSignerUtil.hs256(saTokenConfig.getJwtSecretKey().getBytes()));
tokenInfo.setTokenValue(sign);
StpUtil.setTokenValue(sign);
userEntity.setPwd(null); userEntity.setPwd(null);
return R.ok().putData("tokenInfo", tokenInfo).putData("user", userEntity); return R.ok().putData("tokenInfo", tokenInfo).putData("user", userEntity);
} }

View File

@@ -0,0 +1,136 @@
package net.geedge.asw.module.sys.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysI18nEntity;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysI18nService;
import net.geedge.asw.module.sys.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v1/i18n")
public class SysI18nController {
@Autowired
private ISysUserService userService;
@Autowired
private ISysI18nService sysI18nService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
SysI18nEntity entity = sysI18nService.getById(id);
if (T.ObjectUtil.isNotNull(entity)) {
SysUserEntity user = userService.getById(entity.getUpdateUserId());
user.setPwd(null);
entity.setUpdateUser(user);
}
return R.ok().putData("record", entity);
}
@GetMapping
public R list(String ids, String q, String lang, String code, String value,
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "20") Integer size,
@RequestParam(defaultValue = "name") String orderBy) {
QueryWrapper<SysI18nEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.in(T.StrUtil.isNotEmpty(ids), "id", T.StrUtil.split(ids, ','));
if (T.StrUtil.isNotBlank(q)) {
queryWrapper.and(wrapper -> wrapper.like("lang", q)
.or().like("code", q)
.or().like("value", q)
.or().like("remark", q)
);
}
queryWrapper.eq(T.StrUtil.isNotEmpty(lang), "lang", lang);
queryWrapper.like(T.StrUtil.isNotEmpty(code), "code", code);
queryWrapper.like(T.StrUtil.isNotEmpty(value), "value", value);
Page<SysI18nEntity> page = Page.of(current, size);
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));
page = sysI18nService.page(page, queryWrapper);
return R.ok(page);
}
@PostMapping
public R add(@RequestBody SysI18nEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getCode()).notEmpty()
.and(entity.getValue()).notEmpty()
.and(entity.getLang()).notEmpty();
SysI18nEntity one = sysI18nService.getOne(new LambdaQueryWrapper<SysI18nEntity>()
.eq(SysI18nEntity::getCode, entity.getCode())
.eq(SysI18nEntity::getLang, entity.getLang()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
if (T.ObjectUtil.isEmpty(entity.getName())) {
entity.setName(entity.getCode());
}
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
sysI18nService.save(entity);
return R.ok().putData("id", entity.getId());
}
@PutMapping
public R update(@RequestBody SysI18nEntity entity) {
T.VerifyUtil.is(entity).notNull()
.and(entity.getId()).notEmpty(RCode.ID_CANNOT_EMPTY)
.and(entity.getCode()).notEmpty()
.and(entity.getValue()).notEmpty()
.and(entity.getLang()).notEmpty();
SysI18nEntity one = sysI18nService.getOne(new LambdaQueryWrapper<SysI18nEntity>()
.eq(SysI18nEntity::getCode, entity.getCode())
.eq(SysI18nEntity::getLang, entity.getLang())
.ne(SysI18nEntity::getId, entity.getId()));
if (T.ObjectUtil.isNotNull(one)) {
throw ASWException.builder().rcode(RCode.SYS_DUPLICATE_RECORD).build();
}
if (T.ObjectUtil.isEmpty(entity.getName())) {
entity.setName(entity.getCode());
}
entity.setUpdateTimestamp(System.currentTimeMillis());
entity.setUpdateUserId(StpUtil.getLoginIdAsString());
sysI18nService.updateById(entity);
return R.ok().putData("id", entity.getId());
}
@DeleteMapping
public R delete(String[] ids) {
T.VerifyUtil.is(ids).notEmpty();
sysI18nService.removeBatchByIds(T.ListUtil.of(ids));
return R.ok();
}
@GetMapping("/lang")
public R lang(@RequestParam(required = false, defaultValue = "en,zh") String l) {
List<String> langList = T.StrUtil.split(l, ',');
Map result = T.MapUtil.builder().build();
for (String lang : langList) {
List<SysI18nEntity> dataList = sysI18nService.list(new LambdaQueryWrapper<SysI18nEntity>().eq(SysI18nEntity::getLang, lang));
Map<String, String> collect = dataList.stream().collect(Collectors.toMap(SysI18nEntity::getCode, SysI18nEntity::getValue));
result.put(lang, collect);
}
return R.ok(result);
}
@PutMapping("/clearCache")
public R clearCache() {
sysI18nService.clearCache();
return R.ok();
}
}

View File

@@ -1,29 +1,19 @@
package net.geedge.asw.module.sys.controller; package net.geedge.asw.module.sys.controller;
import org.springframework.beans.factory.annotation.Autowired; import cn.hutool.log.Log;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R; import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T; import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysRoleEntity; import net.geedge.asw.module.sys.entity.SysRoleEntity;
import net.geedge.asw.module.sys.service.ISysRoleService; import net.geedge.asw.module.sys.service.ISysRoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController @RestController
@RequestMapping("/sys/role") @RequestMapping("/api/v1/role")
public class SysRoleController { public class SysRoleController {
private static final Log log = Log.get(); private static final Log log = Log.get();
@@ -37,8 +27,10 @@ public class SysRoleController {
} }
@GetMapping @GetMapping
public R list(String ids, String name, @RequestParam(defaultValue = "1") Integer current, public R list(String ids, String name,
@RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) { @RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "20") Integer size,
@RequestParam(defaultValue = "name") String orderBy) {
QueryWrapper<SysRoleEntity> queryWrapper = new QueryWrapper<SysRoleEntity>(); QueryWrapper<SysRoleEntity> queryWrapper = new QueryWrapper<SysRoleEntity>();
queryWrapper.like(T.StrUtil.isNotBlank(name), "name", name).in(T.StrUtil.isNotBlank(ids), "id", ids.split(",")); queryWrapper.like(T.StrUtil.isNotBlank(name), "name", name).in(T.StrUtil.isNotBlank(ids), "id", ids.split(","));
Page<SysRoleEntity> page = Page.of(current, size); Page<SysRoleEntity> page = Page.of(current, size);

View File

@@ -1,34 +1,24 @@
package net.geedge.asw.module.sys.controller; package net.geedge.asw.module.sys.controller;
import java.util.List; import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.*;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.entity.SysRoleEntity; import net.geedge.asw.module.sys.entity.SysRoleEntity;
import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.entity.SysUserRoleEntity;
import net.geedge.asw.module.sys.service.ISysRoleService; import net.geedge.asw.module.sys.service.ISysRoleService;
import net.geedge.asw.module.sys.service.ISysUserRoleService;
import net.geedge.asw.module.sys.service.ISysUserService; import net.geedge.asw.module.sys.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.stream.Collectors;
@RestController @RestController
@RequestMapping("/sys/user") @RequestMapping("/api/v1/user")
public class SysUserController { public class SysUserController {
private static final Log log = Log.get(); private static final Log log = Log.get();
@@ -36,24 +26,35 @@ public class SysUserController {
private ISysUserService userService; private ISysUserService userService;
@Autowired @Autowired
private ISysRoleService roleService; private ISysRoleService roleService;
@Autowired
private ISysUserRoleService uerRoleService;
@GetMapping("/{id}") @GetMapping("/{id}")
public R detail(@PathVariable("id") String id) { public R detail(@PathVariable("id") String id) {
SysUserEntity entity = userService.getById(id); SysUserEntity entity = userService.getById(id);
entity.setPwd(null); if (T.ObjectUtil.isNotNull(entity)) {
List<SysRoleEntity> roleList = roleService.listByIds(T.ListUtil.of(entity.getRoleIds())); entity.setPwd(null);
entity.setRoles(roleList); List<SysUserRoleEntity> userRoleList = uerRoleService.list(new LambdaQueryWrapper<SysUserRoleEntity>().eq(SysUserRoleEntity::getUserId, entity.getId()));
if (T.CollUtil.isNotEmpty(userRoleList)) {
List<String> roleIds = userRoleList.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList());
List<SysRoleEntity> roleList = roleService.listByIds(roleIds);
entity.setRoles(roleList);
}
}
return R.ok().putData("record", entity); return R.ok().putData("record", entity);
} }
@GetMapping @GetMapping
public R list(String name, @RequestParam(defaultValue = "1") Integer current, public R list(String ids, String q,
@RequestParam(defaultValue = "20") Integer size, @RequestParam(defaultValue = "name") String orderBy) { @RequestParam(defaultValue = "1") Integer current,
QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<SysUserEntity>(); @RequestParam(defaultValue = "20") Integer size,
@RequestParam(defaultValue = "name") String orderBy) {
QueryWrapper<SysUserEntity> queryWrapper = new QueryWrapper<>();
// 不查询 pwd 列 // 不查询 pwd 列
queryWrapper.select(SysUserEntity.class, entity -> !entity.getColumn().equals("pwd")); queryWrapper.select(SysUserEntity.class, entity -> !entity.getColumn().equals("pwd"));
if (T.StrUtil.isNotBlank(name)) { queryWrapper.in(T.StrUtil.isNotEmpty(ids), "id", T.StrUtil.split(ids, ','));
queryWrapper.and(wrapper -> wrapper.like("name", name).or().like("user_name", name)); if (T.StrUtil.isNotBlank(q)) {
queryWrapper.and(wrapper -> wrapper.like("name", q).or().like("user_name", q));
} }
Page<SysUserEntity> page = Page.of(current, size); Page<SysUserEntity> page = Page.of(current, size);
page.addOrder(T.PageUtil.decodeOrderByStr(orderBy)); page.addOrder(T.PageUtil.decodeOrderByStr(orderBy));

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.sys.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.sys.entity.SysI18nEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface SysI18nDao extends BaseMapper<SysI18nEntity> {
}

View File

@@ -0,0 +1,53 @@
package net.geedge.asw.module.sys.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("sys_i18n")
public class SysI18nEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 名称
*/
private String name;
/**
* i18n code
*/
private String code;
/**
* 语言
*/
private String lang;
/**
* 翻译值
*/
private String value;
/**
* 备注信息
*/
private String remark;
/**
* 操作人
*/
private String updateUserId;
/**
* 操作时间
*/
private Long updateTimestamp;
@TableField(exist = false)
private SysUserEntity updateUser;
}

View File

@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import java.util.List;
@Data @Data
@TableName("sys_role") @TableName("sys_role")
public class SysRoleEntity { public class SysRoleEntity {
@@ -20,4 +22,8 @@ public class SysRoleEntity {
@TableField(exist = false) @TableField(exist = false)
private String[] menuIds; private String[] menuIds;
private Long createTimestamp; private Long createTimestamp;
@TableField(exist = false)
private List<SysMenuEntity> menus;
@TableField(exist = false)
private List<String> buttons;
} }

View File

@@ -15,13 +15,21 @@ public class SysUserEntity {
@TableId(type = IdType.ASSIGN_UUID) @TableId(type = IdType.ASSIGN_UUID)
private String id; private String id;
private String name; private String name;
@TableField("user_name") @TableField("user_name")
private String userName; private String userName;
private String pwd; private String pwd;
@TableField(exist = false) @TableField(exist = false)
private String[] roleIds; private String roleIds;
@TableField(exist = false) @TableField(exist = false)
private List<SysRoleEntity> roles; private List<SysRoleEntity> roles;
private String accessLevel;
private Long createTimestamp; private Long createTimestamp;
} }

View File

@@ -0,0 +1,23 @@
package net.geedge.asw.module.sys.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.sys.entity.SysI18nEntity;
import java.util.Locale;
public interface ISysI18nService extends IService<SysI18nEntity> {
String queryValue(String code);
String queryValue(String code, Object... param);
String queryValue(String code, String lang);
String queryValue(String code, Locale locale, Object... param);
String queryValueByName(String name, Object... param);
void clearCache();
}

View File

@@ -1,16 +1,12 @@
package net.geedge.asw.module.sys.service.impl; package net.geedge.asw.module.sys.service.impl;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Pair;
import cn.hutool.core.map.MapUtil;
import cn.hutool.log.Log; import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants; import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.RCode;
@@ -21,61 +17,107 @@ import net.geedge.asw.module.sys.entity.SysMenuEntity;
import net.geedge.asw.module.sys.entity.SysRoleEntity; import net.geedge.asw.module.sys.entity.SysRoleEntity;
import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysAuthService; import net.geedge.asw.module.sys.service.ISysAuthService;
import net.geedge.asw.module.workbook.entity.WorkbookMemberEntity;
import net.geedge.asw.module.workbook.service.IWorkbookMemberService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceMemberService;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service @Service
public class SysAuthServiceImpl implements ISysAuthService { public class SysAuthServiceImpl implements ISysAuthService {
private static final Log log = Log.get(); private static final Log log = Log.get();
@Autowired @Autowired
private SysUserDao userDao; private SysUserDao userDao;
@Autowired
private SysRoleDao roleDao;
@Override @Autowired
public SysUserEntity login(String userName, String pwd) { private SysRoleDao roleDao;
SysUserEntity userEntity = userDao
.selectOne(new QueryWrapper<SysUserEntity>().lambda().eq(SysUserEntity::getUserName, userName));
if (T.ObjectUtil.isNull(userEntity)
|| !T.StrUtil.equals(userEntity.getPwd(), T.AesUtil.encrypt(pwd, Constants.AES_KEY))) {
log.warn("user login error, username: {}", userName);
throw ASWException.builder().rcode(RCode.SYS_USER_PWD_ERROR).build();
}
StpUtil.login(userEntity.getId());
log.info("user login success, userName: {}", userName);
return userEntity;
}
@Override @Autowired
public void logout() { private IWorkspaceService workspaceService;
StpUtil.logout();
}
/** @Autowired
* 获取登录用户权限 private IWorkspaceMemberService workspaceMemberService;
*/
@Override @Override
public Map<String, Object> userPermissions() { public SysUserEntity login(String userName, String pwd) {
Map<String, Object> result = T.MapUtil.newHashMap(); SysUserEntity userEntity = userDao
String userId = StpUtil.getLoginIdAsString(); .selectOne(new QueryWrapper<SysUserEntity>().lambda().eq(SysUserEntity::getUserName, userName));
List<SysRoleEntity> roleList = roleDao.findRoleByUserId(userId); if (T.ObjectUtil.isNull(userEntity)
result.put("roles", roleList); || !T.StrUtil.equals(userEntity.getPwd(), T.AesUtil.encrypt(pwd, Constants.AES_KEY))) {
// 组织 menu数据 log.warn("user login error, username: {}", userName);
List<SysMenuEntity> menuList = roleDao.findMenuByUserId(userId); throw ASWException.builder().rcode(RCode.SYS_USER_PWD_ERROR).build();
List<String> buttonList = menuList.stream().filter(menu -> T.StrUtil.equalsIgnoreCase(menu.getType(), "button")) }
.map(menu -> menu.getName()).collect(Collectors.toList()); StpUtil.login(userEntity.getId());
result.put("buttons", buttonList); log.info("user login success, userName: {}", userName);
//生成 menu tree结构 return userEntity;
Map<String, List<SysMenuEntity>> groupMap = menuList.stream() }
.filter(menu -> T.StrUtil.equalsIgnoreCase(menu.getType(), "menu"))
.collect(Collectors.groupingBy(SysMenuEntity::getPid)); @Override
menuList.forEach(menu -> { public void logout() {
menu.setChildren(groupMap.get(menu.getId())); StpUtil.logout();
}); }
List<SysMenuEntity> collect = menuList.stream().filter(menu -> T.StrUtil.isBlank(menu.getPid()))
.collect(Collectors.toList()); /**
result.put("menus", collect); * 获取登录用户权限
*/
return result; @Override
} public Map<String, Object> userPermissions() {
String userId = StpUtil.getLoginIdAsString();
SysUserEntity sysUserEntity = userDao.selectById(userId);
String accessLevel = sysUserEntity.getAccessLevel();
List<WorkspaceEntity> workspaceEntityList = workspaceService.list();
if (accessLevel.equalsIgnoreCase("regular")) {
List<WorkspaceMemberEntity> workbookMemberEntityList = workspaceMemberService.list(new LambdaQueryWrapper<WorkspaceMemberEntity>().eq(WorkspaceMemberEntity::getUserId, userId));
List<String> workspaceIdList = workbookMemberEntityList.stream().map(x -> x.getWorkspaceId()).toList();
workspaceEntityList = workspaceService.list(new LambdaQueryWrapper<WorkspaceEntity>().in(WorkspaceEntity::getId, workspaceIdList));
// public workspace
List<WorkspaceEntity> publicWorkspaces = workspaceService.list(new LambdaQueryWrapper<WorkspaceEntity>().eq(WorkspaceEntity::getVisibility, "public"));
workspaceEntityList.addAll(publicWorkspaces);
}
SysRoleEntity role = roleDao.findRoleByUserId(userId).get(0);
// 组织 button 数据
List<SysMenuEntity> menuList = roleDao.findMenuByUserId(userId);
List<String> buttonList = menuList.stream().filter(menu -> T.StrUtil.equalsIgnoreCase(menu.getType(), "button"))
.map(menu -> menu.getName()).collect(Collectors.toList());
role.setButtons(buttonList);
//生成 menu tree结构
Map<String, List<SysMenuEntity>> groupMap = menuList.stream()
.filter(menu -> !T.StrUtil.equalsIgnoreCase(menu.getPid(), "0"))
.collect(Collectors.groupingBy(SysMenuEntity::getPid));
menuList.forEach(menu -> {
menu.setChildren(groupMap.get(menu.getId()));
});
List<SysMenuEntity> collect = menuList.stream()
.filter(menu -> T.StrUtil.equals(menu.getPid(), "0"))
.filter(menu -> T.StrUtil.equals(menu.getType(), "menu"))
.collect(Collectors.toList());
role.setMenus(collect);
List records = ListUtil.list(false);
for (WorkspaceEntity workspace : workspaceEntityList) {
Map<Object, Object> map = MapUtil.builder()
.put("workspace", workspace)
.put("role", role)
.build();
records.add(map);
}
Map<String, Object> result = T.MapUtil.newHashMap();
result.put("records", records);
result.put("accessLevel", accessLevel);
return result;
}
} }

View File

@@ -0,0 +1,69 @@
package net.geedge.asw.module.sys.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.config.I18nConfig;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.dao.SysI18nDao;
import net.geedge.asw.module.sys.entity.SysI18nEntity;
import net.geedge.asw.module.sys.service.ISysI18nService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Locale;
@Service
public class SysI18nServiceImpl extends ServiceImpl<SysI18nDao, SysI18nEntity> implements ISysI18nService {
@Autowired
private I18nConfig i18nConfig;
@Override
public String queryValue(String code) {
return this.queryValue(code, this.getLocale());
}
@Override
public String queryValue(String code, Object... param) {
return this.queryValue(code, this.getLocale(), param);
}
@Override
public String queryValue(String code, String lang) {
return this.queryValue(code, Locale.of(lang));
}
@Override
public String queryValue(String code, Locale locale, Object... param) {
return i18nConfig.getSourceFromCache(code, locale, param);
}
@Override
public String queryValueByName(String name, Object... param) {
String language = this.getLocale().getLanguage();
List<SysI18nEntity> list = this.list(new LambdaQueryWrapper<SysI18nEntity>().eq(SysI18nEntity::getName, name).eq(SysI18nEntity::getLang, language));
if (T.CollUtil.isEmpty(list)) {
return StrUtil.EMPTY;
}
SysI18nEntity sysI18nEntity = list.stream().findFirst().get();
return this.queryValue(sysI18nEntity.getCode(), this.getLocale(), param);
}
@Override
public void clearCache() {
i18nConfig.reload();
}
/**
* getLocale
*
* @return
*/
private Locale getLocale() {
return LocaleContextHolder.getLocale();
}
}

View File

@@ -1,20 +1,17 @@
package net.geedge.asw.module.sys.service.impl; package net.geedge.asw.module.sys.service.impl;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.T; import net.geedge.asw.common.util.T;
import net.geedge.asw.module.sys.dao.SysUserDao; import net.geedge.asw.module.sys.dao.SysUserDao;
import net.geedge.asw.module.sys.entity.SysUserEntity; import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.entity.SysUserRoleEntity; import net.geedge.asw.module.sys.entity.SysUserRoleEntity;
import net.geedge.asw.module.sys.service.ISysUserService; import net.geedge.asw.module.sys.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service @Service
public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> implements ISysUserService { public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> implements ISysUserService {
@@ -32,9 +29,9 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserDao, SysUserEntity> i
this.saveOrUpdate(entity); this.saveOrUpdate(entity);
// 保存 user role关系 // 保存 user role关系
List<SysUserRoleEntity> urList = T.ListUtil.list(false); List<SysUserRoleEntity> urList = T.ListUtil.list(false);
Stream.of(entity.getRoleIds()).forEach(roleId -> { for (String roleId : T.StrUtil.split(entity.getRoleIds(), ",")) {
urList.add(SysUserRoleEntity.builder().roleId(roleId).userId(entity.getId()).build()); urList.add(SysUserRoleEntity.builder().roleId(roleId).userId(entity.getId()).build());
}); }
userRoleService.saveBatch(urList); userRoleService.saveBatch(urList);
} }

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.workbook.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.workbook.entity.WorkbookEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WorkbookDao extends BaseMapper<WorkbookEntity>{
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.workbook.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.workbook.entity.WorkbookMemberEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WorkbookMemberDao extends BaseMapper<WorkbookMemberEntity>{
}

View File

@@ -0,0 +1,10 @@
package net.geedge.asw.module.workbook.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.workbook.entity.WorkbookResourceEntity;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface WorkbookResourceDao extends BaseMapper<WorkbookResourceEntity>{
}

View File

@@ -0,0 +1,24 @@
package net.geedge.asw.module.workbook.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("workbook")
public class WorkbookEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String tags;
private String visibility;
private String description;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
}

View File

@@ -0,0 +1,19 @@
package net.geedge.asw.module.workbook.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("workbook_member")
public class WorkbookMemberEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String workbookId;
private String userId;
private Long createTimestamp;
private String createUserId;
}

View File

@@ -0,0 +1,18 @@
package net.geedge.asw.module.workbook.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("workbook_resource")
public class WorkbookResourceEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String workbookId;
private String resourceType;
private String resourceId;
}

View File

@@ -0,0 +1,8 @@
package net.geedge.asw.module.workbook.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.workbook.entity.WorkbookMemberEntity;
public interface IWorkbookMemberService extends IService<WorkbookMemberEntity>{
}

View File

@@ -0,0 +1,14 @@
package net.geedge.asw.module.workbook.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.workbook.entity.WorkbookResourceEntity;
import java.util.List;
public interface IWorkbookResourceService extends IService<WorkbookResourceEntity>{
void saveResource(String workbookId, String resourceId, String type);
void removeResource(List<String> resourceIdList, String type);
}

View File

@@ -0,0 +1,8 @@
package net.geedge.asw.module.workbook.service;
import com.baomidou.mybatisplus.extension.service.IService;
import net.geedge.asw.module.workbook.entity.WorkbookEntity;
public interface IWorkbookService extends IService<WorkbookEntity>{
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.workbook.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.module.workbook.dao.WorkbookMemberDao;
import net.geedge.asw.module.workbook.entity.WorkbookMemberEntity;
import net.geedge.asw.module.workbook.service.IWorkbookMemberService;
import org.springframework.stereotype.Service;
@Service
public class WorkbookMemberServiceImpl extends ServiceImpl<WorkbookMemberDao, WorkbookMemberEntity> implements IWorkbookMemberService {
}

View File

@@ -0,0 +1,50 @@
package net.geedge.asw.module.workbook.service.impl;
import cn.hutool.log.Log;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.workbook.dao.WorkbookResourceDao;
import net.geedge.asw.module.workbook.entity.WorkbookResourceEntity;
import net.geedge.asw.module.workbook.service.IWorkbookResourceService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class WorkbookResourceServiceImpl extends ServiceImpl<WorkbookResourceDao, WorkbookResourceEntity> implements IWorkbookResourceService {
private static final Log log = Log.get();
@Override
public void saveResource(String wbId, String resId, String type) {
if (T.StrUtil.hasEmpty(wbId, resId, type)) {
return;
}
// remove
this.remove(new LambdaQueryWrapper<WorkbookResourceEntity>()
.eq(WorkbookResourceEntity::getResourceId, resId)
.eq(WorkbookResourceEntity::getResourceType, type));
// insert
WorkbookResourceEntity res = new WorkbookResourceEntity();
res.setWorkbookId(wbId);
res.setResourceId(resId);
res.setResourceType(type);
this.save(res);
}
@Override
public void removeResource(List<String> resIdList, String type) {
if (T.CollUtil.isEmpty(resIdList) || T.StrUtil.isEmpty(type)) {
return;
}
// remove
this.remove(new LambdaQueryWrapper<WorkbookResourceEntity>()
.eq(WorkbookResourceEntity::getResourceType, type)
.in(WorkbookResourceEntity::getResourceId, resIdList));
}
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.workbook.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import net.geedge.asw.module.workbook.dao.WorkbookDao;
import net.geedge.asw.module.workbook.entity.WorkbookEntity;
import net.geedge.asw.module.workbook.service.IWorkbookService;
import org.springframework.stereotype.Service;
@Service
public class WorkbookServiceImpl extends ServiceImpl<WorkbookDao, WorkbookEntity> implements IWorkbookService {
}

View File

@@ -0,0 +1,41 @@
package net.geedge.asw.module.workbook.util;
import cn.hutool.core.util.StrUtil;
public class WorkbookConstant {
/**
* resource type
*/
public enum ResourceType {
PACKAGE("package"),
SIGNATURE("signature"),
JOB("job"),
PCAP("pcap");
private String value;
ResourceType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public static ResourceType getInstanceByType(String type) {
for (ResourceType v : values()) {
if (StrUtil.equalsIgnoreCase(v.getValue(), type)) {
return v;
}
}
throw new IllegalArgumentException("Failed to find resource type with parameter.");
}
}
}

View File

@@ -0,0 +1,121 @@
package net.geedge.asw.module.workspace.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.common.util.VerifyUtil;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import net.geedge.asw.module.sys.service.ISysUserService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceMemberService;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/v1/workspace")
public class WorkspaceController {
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IWorkspaceMemberService workspaceMemberService;
@Autowired
private ISysUserService userService;
@GetMapping("/{id}")
public R detail(@PathVariable("id") String id) {
WorkspaceEntity workspace = workspaceService.getById(id);
String createUserId = workspace.getCreateUserId();
String updateUserId = workspace.getUpdateUserId();
if (T.StrUtil.isNotEmpty(createUserId)) {
SysUserEntity createUser = userService.getById(createUserId);
workspace.setCreateUser(createUser);
}
if (T.StrUtil.isNotEmpty(updateUserId)) {
SysUserEntity updateUser = userService.getById(updateUserId);
workspace.setUpdateUser(updateUser);
}
return R.ok().putData("record", workspace);
}
@GetMapping
public R list(@RequestParam Map<String, Object> params) {
Page page = workspaceService.queryList(params);
return R.ok(page);
}
@PostMapping
public R save(@RequestBody WorkspaceEntity workspace) {
VerifyUtil.is(workspace).notNull()
.and(workspace.getName()).notEmpty(RCode.WORKSPACE_NAME_CANNOT_EMPTY)
.and(workspace.getVisibility()).notEmpty(RCode.WORKSPACE_VISIBILITY_CANNOT_EMPTY);
WorkspaceEntity workspaceEntity = workspaceService.saveWorkspace(workspace);
return R.ok().putData("record", workspaceEntity.getId());
}
@PutMapping
public R update(@RequestBody WorkspaceEntity workspace) {
VerifyUtil.is(workspace).notNull()
.and(workspace.getId()).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY)
.and(workspace.getName()).notEmpty(RCode.WORKSPACE_NAME_CANNOT_EMPTY)
.and(workspace.getVisibility()).notEmpty(RCode.WORKSPACE_VISIBILITY_CANNOT_EMPTY);
WorkspaceEntity workspaceEntity = workspaceService.updateWorkspace(workspace);
return R.ok().putData("record", workspaceEntity.getId());
}
@DeleteMapping
public R delete(@RequestParam String ids) {
VerifyUtil.is(ids).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
workspaceService.deleteWorkspace(ids);
return R.ok();
}
@GetMapping("/{workspaceId}/member")
public R getMember(@PathVariable String workspaceId) {
List<WorkspaceMemberEntity> memberEntityList = workspaceMemberService.queryList(workspaceId);
return R.ok().putData("record", memberEntityList);
}
@PostMapping("/{workspaceId}/member")
public R saveMember(@PathVariable String workspaceId, @RequestBody List<WorkspaceMemberEntity> workspaceMembers) {
VerifyUtil.is(workspaceMembers).notEmpty(RCode.WORKSPACE_MEMBER_CANNOT_EMPTY)
.and(workspaceId).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
List<WorkspaceMemberEntity> workspaceMemberEntityList = workspaceMemberService.saveMember(workspaceId, workspaceMembers);
return R.ok().putData("record", workspaceMemberEntityList);
}
@PutMapping("/{workspaceId}/member")
public R updateMember(@PathVariable String workspaceId, @RequestBody List<WorkspaceMemberEntity> workspaceMembers) {
VerifyUtil.is(workspaceMembers).notEmpty(RCode.WORKSPACE_MEMBER_CANNOT_EMPTY)
.and(workspaceId).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY);
List<WorkspaceMemberEntity> workspaceMemberEntityList = workspaceMemberService.updateMember(workspaceId, workspaceMembers);
return R.ok().putData("record", workspaceMemberEntityList);
}
@DeleteMapping("/{workspaceId}/member")
public R deleteMember(@PathVariable String workspaceId, @RequestParam String userIds) {
VerifyUtil.is(userIds).notEmpty(RCode.USER_ID_CANNOT_EMPTY);
List<String> list = Arrays.asList(userIds.split(","));
workspaceMemberService.remove(new LambdaQueryWrapper<WorkspaceMemberEntity>()
.eq(WorkspaceMemberEntity::getWorkspaceId, workspaceId)
.in(WorkspaceMemberEntity::getUserId, list));
return R.ok();
}
}

View File

@@ -0,0 +1,15 @@
package net.geedge.asw.module.workspace.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import java.util.Map;
@Mapper
public interface WorkspaceDao extends BaseMapper<WorkspaceEntity> {
List<WorkspaceEntity> queryList(Page page, Map<String, Object> params);
}

View File

@@ -0,0 +1,13 @@
package net.geedge.asw.module.workspace.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface WorkspaceMemberDao extends BaseMapper<WorkspaceMemberEntity> {
List<WorkspaceMemberEntity> queryList(String workspaceId);
}

View File

@@ -0,0 +1,38 @@
package net.geedge.asw.module.workspace.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import net.geedge.asw.module.sys.entity.SysRoleEntity;
import net.geedge.asw.module.sys.entity.SysUserEntity;
import java.util.List;
@Data
@TableName("workspace")
public class WorkspaceEntity {
@TableId(type = IdType.ASSIGN_UUID)
private String id;
private String name;
private String tags;
private String visibility;
private String description;
private Long createTimestamp;
private Long updateTimestamp;
private String createUserId;
private String updateUserId;
@TableField(exist = false)
private SysUserEntity createUser;
@TableField(exist = false)
private SysUserEntity updateUser;
@TableField(exist = false)
private List<WorkspaceMemberEntity> members;
}

Some files were not shown because too many files have changed in this diff Show More