diff --git a/src/main/java/net/geedge/asw/common/util/Constants.java b/src/main/java/net/geedge/asw/common/util/Constants.java index f81bab1..2fe4f98 100644 --- a/src/main/java/net/geedge/asw/common/util/Constants.java +++ b/src/main/java/net/geedge/asw/common/util/Constants.java @@ -104,7 +104,10 @@ public class Constants { public static final Map ENV_TERMINAL_WEBSOCKET_SESSION = T.MapUtil.newHashMap(); - + /** + * Android package type + */ + public static final List ANDROID_PACKAGE_TYPE_LIST = T.ListUtil.of("xapk", "apk"); public static final String EMPTY_FILE_MD5 = "d41d8cd98f00b204e9800998ecf8427e"; } diff --git a/src/main/java/net/geedge/asw/common/util/RCode.java b/src/main/java/net/geedge/asw/common/util/RCode.java index f93779c..36e1bf7 100644 --- a/src/main/java/net/geedge/asw/common/util/RCode.java +++ b/src/main/java/net/geedge/asw/common/util/RCode.java @@ -55,6 +55,7 @@ public enum RCode { // Package PACKAGE_ID_CANNOT_EMPTY(202001, "package id cannot be empty"), PACKAGE_DESCRIPTION_CANNOT_EMPTY(202002, "package description cannot be empty"), + PACKAGE_FILE_TYPE_ERROR(202003, "package invalid file"), // GIT diff --git a/src/main/java/net/geedge/asw/module/app/service/impl/PackageServiceImpl.java b/src/main/java/net/geedge/asw/module/app/service/impl/PackageServiceImpl.java index d4a0ea5..9f862fa 100644 --- a/src/main/java/net/geedge/asw/module/app/service/impl/PackageServiceImpl.java +++ b/src/main/java/net/geedge/asw/module/app/service/impl/PackageServiceImpl.java @@ -1,9 +1,12 @@ 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.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.Constants; import net.geedge.asw.common.util.RCode; import net.geedge.asw.common.util.T; import net.geedge.asw.module.app.dao.PackageDao; @@ -64,48 +67,56 @@ public class PackageServiceImpl extends ServiceImpl i } @Override + @Transactional(rollbackFor = Exception.class) public PackageEntity savePackage(String workspaceId, String description, Resource fileResource) { + + String pkgId = T.StrUtil.uuid(); + String filename = fileResource.getFilename(); + String suffix = T.FileUtil.extName(filename); + suffix = T.StrUtil.emptyToDefault(suffix, "apk"); + if (!Constants.ANDROID_PACKAGE_TYPE_LIST.contains(suffix)) { + throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR); + } + String saveFileName = pkgId + "." + suffix; + File destination = T.FileUtil.file(PkgConstant.APK_FILES_DIR, saveFileName); PackageEntity entity = new PackageEntity(); + ApkUtil apkUtil = new ApkUtil(); + apkUtil.setAaptToolPath(Path.of(T.WebPathUtil.getRootPath(), "lib", "aapt").toString()); try { - String pkgId = T.StrUtil.uuid(); - entity.setId(pkgId); - entity.setName(fileResource.getFilename()); - entity.setDescription(T.StrUtil.emptyToDefault(description, "")); - - // 默认安卓 - entity.setPlatform(PkgConstant.Platform.ANDROID.getValue()); - - entity.setWorkspaceId(workspaceId); - entity.setCreateUserId(StpUtil.getLoginIdAsString()); - entity.setUpdateUserId(StpUtil.getLoginIdAsString()); - entity.setCreateTimestamp(System.currentTimeMillis()); - entity.setUpdateTimestamp(System.currentTimeMillis()); - - byte[] bytes = fileResource.getInputStream().readAllBytes(); - entity.setSize((long) bytes.length); - - // path - String fileExtName = T.StrUtil.emptyToDefault(T.FileUtil.extName(fileResource.getFilename()), "pcap"); - String saveFileName = pkgId + "." + fileExtName; - File destination = T.FileUtil.file(PkgConstant.APK_FILES_DIR, saveFileName); FileUtils.copyInputStreamToFile(fileResource.getInputStream(), destination); - entity.setPath(destination.getPath()); - - // parse - try { - ApkUtil apkUtil = new ApkUtil(); - apkUtil.setAaptToolPath(Path.of(T.WebPathUtil.getRootPath(), "lib", "aapt").toString()); - ApkInfo apkInfo = apkUtil.parseApk(entity.getPath()); + if (suffix.equals("apk")) { + // parse + ApkInfo apkInfo = apkUtil.parseApk(destination.getPath()); + if (T.ObjectUtil.isNull(apkInfo)) { + throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR); + } entity.setVersion(apkInfo.getVersionName()); entity.setIdentifier(apkInfo.getPackageName()); - } catch (Exception e) { - log.error(e, "[savePackage] [parse error] [file: {}]", fileResource.getFilename()); + } else { + ApkInfo apkInfo = apkUtil.parseXapk(destination.getPath()); + if (T.ObjectUtil.isNull(apkInfo)) { + throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR); + } + entity.setVersion(apkInfo.getSdkVersion()); + entity.setIdentifier(apkInfo.getPackageName()); } - - this.save(entity); } catch (Exception e) { - log.error(e, "[savePackage] [error] [file: {}]", fileResource.getFilename()); + log.error(e, "[savePackage] [save package error] [file: {}]", fileResource.getFilename()); + FileUtil.del(destination); + throw new ASWException(RCode.PACKAGE_FILE_TYPE_ERROR); } + entity.setId(pkgId); + entity.setName(fileResource.getFilename()); + entity.setDescription(T.StrUtil.emptyToDefault(description, "")); + entity.setPlatform(PkgConstant.Platform.ANDROID.getValue()); + entity.setWorkspaceId(workspaceId); + entity.setCreateUserId(StpUtil.getLoginIdAsString()); + entity.setUpdateUserId(StpUtil.getLoginIdAsString()); + entity.setCreateTimestamp(System.currentTimeMillis()); + entity.setUpdateTimestamp(System.currentTimeMillis()); + entity.setSize(destination.length()); + entity.setPath(destination.getPath()); + this.save(entity); return entity; } diff --git a/src/main/java/net/geedge/asw/module/app/util/ApkUtil.java b/src/main/java/net/geedge/asw/module/app/util/ApkUtil.java index 1d34e79..b969591 100644 --- a/src/main/java/net/geedge/asw/module/app/util/ApkUtil.java +++ b/src/main/java/net/geedge/asw/module/app/util/ApkUtil.java @@ -1,9 +1,14 @@ package net.geedge.asw.module.app.util; +import cn.hutool.core.io.FileUtil; import cn.hutool.log.Log; +import net.geedge.asw.common.util.Constants; import net.geedge.asw.common.util.T; import java.io.*; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; public class ApkUtil { @@ -60,6 +65,9 @@ public class ApkUtil { while ((temp = bufferedReader.readLine()) != null) { setApkInfoProperty(apkInfo, temp); } + if (T.StrUtil.isBlank(apkInfo.getPackageName()) || T.StrUtil.isBlank(apkInfo.getVersionName())) { + return null; + } return apkInfo; } catch (IOException e) { log.error(e, "[parseApk] [error] [path: {}]", apkPath); @@ -73,6 +81,43 @@ public class ApkUtil { } } + public ApkInfo parseXapk(String xapkPath) { + InputStream inputStream = null; + BufferedReader reader = null; + File tempFile = null; + try { + ZipFile zipFile = new ZipFile(T.FileUtil.file(xapkPath)); + ZipEntry entry = zipFile.getEntry("manifest.json"); + inputStream = zipFile.getInputStream(entry); + StringBuilder manifestJson = new StringBuilder(); + reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + manifestJson.append(line).append("\n"); + } + Map manifest = T.JSONUtil.toBean(manifestJson.toString(), Map.class); + ZipEntry packageFile = zipFile.getEntry(T.StrUtil.concat(true, T.MapUtil.getStr(manifest, "package_name"), ".apk")); + tempFile = T.FileUtil.file(Constants.TEMP_PATH, packageFile.getName()); + FileUtil.writeBytes(zipFile.getInputStream(packageFile).readAllBytes(), tempFile); + ApkInfo apkInfo = this.parseApk(tempFile.getPath()); + if (apkInfo == null) { + return null; + } + if (!T.BooleanUtil.and(apkInfo.getVersionName().equals(T.MapUtil.getStr(manifest, "version_name")), + apkInfo.getPackageName().equals(T.MapUtil.getStr(manifest, "package_name")))) { + return null; + } + return apkInfo; + } catch (Exception e) { + log.error(e, "[parseXapk] [error] [path: {}]", xapkPath); + return null; + } finally { + T.FileUtil.del(tempFile); + T.IoUtil.close(inputStream); + T.IoUtil.close(reader); + } + } + private void setApkInfoProperty(ApkInfo apkInfo, String source) { if (source.startsWith(APPLICATION)) { String[] rs = source.split("( icon=')|'"); diff --git a/src/main/java/net/geedge/asw/module/runner/controller/JobController.java b/src/main/java/net/geedge/asw/module/runner/controller/JobController.java index 8e1771e..f0f886d 100644 --- a/src/main/java/net/geedge/asw/module/runner/controller/JobController.java +++ b/src/main/java/net/geedge/asw/module/runner/controller/JobController.java @@ -32,7 +32,8 @@ public class JobController { public R list(@PathVariable("workspaceId") String workspaceId, @RequestParam Map params) { T.VerifyUtil.is(params).notNull() - .and(T.MapUtil.getStr(params, "workspaceId")).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY); + .and(workspaceId).notEmpty(RCode.WORKSPACE_ID_CANNOT_EMPTY); + params.put("workspaceId", workspaceId); Page page = jobService.queryList(params); return R.ok(page); } diff --git a/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceMemberServiceImpl.java b/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceMemberServiceImpl.java index 161d904..2b05fc9 100644 --- a/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceMemberServiceImpl.java +++ b/src/main/java/net/geedge/asw/module/workspace/service/impl/WorkspaceMemberServiceImpl.java @@ -2,6 +2,7 @@ package net.geedge.asw.module.workspace.service.impl; import cn.dev33.satoken.stp.StpUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import net.geedge.asw.common.util.ASWException; import net.geedge.asw.common.util.RCode; @@ -50,9 +51,22 @@ public class WorkspaceMemberServiceImpl extends ServiceImpl updateMember(String workspaceId, List memberList) { - validateInfo(workspaceId, memberList); - workspaceMemberService.remove(new LambdaQueryWrapper().eq(WorkspaceMemberEntity::getWorkspaceId, workspaceId)); - workspaceMemberService.saveBatch(memberList); + List list = this.list(new LambdaQueryWrapper().eq(WorkspaceMemberEntity::getWorkspaceId, workspaceId)); + List userIds = list.stream().map(x -> x.getUserId()).toList(); + for (WorkspaceMemberEntity member : memberList) { + String userId = member.getUserId(); + if (userIds.contains(userId)) { + this.update(new LambdaUpdateWrapper() + .eq(WorkspaceMemberEntity::getUserId, userId) + .eq(WorkspaceMemberEntity::getWorkspaceId, workspaceId) + .set(WorkspaceMemberEntity::getRoleId, member.getRoleId())); + }else { + member.setWorkspaceId(workspaceId); + member.setCreateTimestamp(System.currentTimeMillis()); + member.setCreateUserId(StpUtil.getLoginIdAsString()); + this.save(member); + } + } Map params = T.MapUtil.builder("workspaceId", workspaceId).build(); List memberEntityList = workspaceMemberService.queryList(params); return memberEntityList; diff --git a/src/main/resources/db/migration/R__AZ_sys_menu.sql b/src/main/resources/db/migration/R__AZ_sys_menu.sql index 5dae931..93b5013 100644 --- a/src/main/resources/db/migration/R__AZ_sys_menu.sql +++ b/src/main/resources/db/migration/R__AZ_sys_menu.sql @@ -29,7 +29,7 @@ INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, ` INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('4003', 'package_delete', 'buttons.delete', '4000', 'button', '', '', '', 3, 1722478572000, 1); INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('4004', 'package_download', 'buttons.download', '4000', 'button', '', '', '', 4, 1722478572000, 1); -INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('5000', 'jobs', 'overall.jobs', '0', 'menu', '', '/jobs', 'asw-icon icon-Jobs', 4, 1722478572000, 0); +INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('5000', 'jobs', 'overall.jobs', '0', 'menu', '', '/jobs', 'asw-icon icon-Jobs', 4, 1722478572000, 1); INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('5001', 'job_view', 'buttons.view', '5000', 'button', '', '', '', 1, 1722478572000, 1); INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('5002', 'job_add', 'buttons.add', '5000', 'button', '', '', '', 2, 1722478572000, 1); INSERT INTO `sys_menu` (`id`, `name`, `i18n`, `pid`, `type`, `perms`, `route`, `icon`, `order`, `create_timestamp`, `state`) VALUES ('5003', 'job_delete', 'buttons.delete', '5000', 'button', '', '', '', 3, 1722478572000, 1);