feat(系统设置): 增加SSL证书认证
This commit is contained in:
parent
c8b3d4573c
commit
02e4299454
|
@ -2,7 +2,10 @@ package io.metersphere.api.controller;
|
|||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.api.dto.ApiTestEnvironmentDTO;
|
||||
import io.metersphere.api.dto.ssl.KeyStoreEntry;
|
||||
import io.metersphere.api.service.ApiTestEnvironmentService;
|
||||
import io.metersphere.api.service.CommandService;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
|
@ -12,6 +15,7 @@ import io.metersphere.service.CheckPermissionService;
|
|||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
@ -25,6 +29,8 @@ public class ApiTestEnvironmentController {
|
|||
ApiTestEnvironmentService apiTestEnvironmentService;
|
||||
@Resource
|
||||
private CheckPermissionService checkPermissionService;
|
||||
@Resource
|
||||
private CommandService commandService;
|
||||
|
||||
@GetMapping("/list/{projectId}")
|
||||
public List<ApiTestEnvironmentWithBLOBs> list(@PathVariable String projectId) {
|
||||
|
@ -34,6 +40,7 @@ public class ApiTestEnvironmentController {
|
|||
|
||||
/**
|
||||
* 查询指定项目和指定名称的环境
|
||||
*
|
||||
* @param goPage
|
||||
* @param pageSize
|
||||
* @param environmentRequest
|
||||
|
@ -54,16 +61,23 @@ public class ApiTestEnvironmentController {
|
|||
return apiTestEnvironmentService.get(id);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping(value = "/get/entry")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
||||
public List<KeyStoreEntry> getEntry(@RequestPart("request") String password, @RequestPart(value = "file") MultipartFile sslFiles) {
|
||||
return commandService.get(password, sslFiles);
|
||||
}
|
||||
|
||||
@PostMapping("/add")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
||||
public String add(@RequestBody ApiTestEnvironmentWithBLOBs apiTestEnvironmentWithBLOBs) {
|
||||
return apiTestEnvironmentService.add(apiTestEnvironmentWithBLOBs);
|
||||
public String create(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironmentWithBLOBs, @RequestPart(value = "files") List<MultipartFile> sslFiles) {
|
||||
return apiTestEnvironmentService.add(apiTestEnvironmentWithBLOBs, sslFiles);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/update")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
||||
public void update(@RequestBody ApiTestEnvironmentWithBLOBs apiTestEnvironment) {
|
||||
apiTestEnvironmentService.update(apiTestEnvironment);
|
||||
public void update(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironment, @RequestPart(value = "files") List<MultipartFile> sslFiles) {
|
||||
apiTestEnvironmentService.update(apiTestEnvironment, sslFiles);
|
||||
}
|
||||
|
||||
@GetMapping("/delete/{id}")
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.api.dto;
|
||||
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ApiTestEnvironmentDTO extends ApiTestEnvironmentWithBLOBs {
|
||||
private List<String> uploadIds;
|
||||
}
|
|
@ -2,10 +2,12 @@ package io.metersphere.api.dto.definition.request;
|
|||
|
||||
import io.metersphere.api.dto.definition.request.variable.ScenarioVariable;
|
||||
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
|
||||
import io.metersphere.api.dto.ssl.MsKeyStore;
|
||||
import io.metersphere.commons.utils.ScriptEngineUtils;
|
||||
import lombok.Data;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -15,6 +17,10 @@ public class ParameterConfig {
|
|||
* 环境配置
|
||||
*/
|
||||
private Map<String, EnvironmentConfig> config;
|
||||
/**
|
||||
* 缓存同一批请求的认证信息
|
||||
*/
|
||||
private Map<String, MsKeyStore> keyStoreMap = new HashMap<>();
|
||||
/**
|
||||
* 公共场景参数
|
||||
*/
|
||||
|
|
|
@ -14,8 +14,11 @@ import io.metersphere.api.dto.scenario.Body;
|
|||
import io.metersphere.api.dto.scenario.HttpConfig;
|
||||
import io.metersphere.api.dto.scenario.HttpConfigCondition;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.ssl.KeyStoreFile;
|
||||
import io.metersphere.api.dto.ssl.MsKeyStore;
|
||||
import io.metersphere.api.service.ApiDefinitionService;
|
||||
import io.metersphere.api.service.ApiTestCaseService;
|
||||
import io.metersphere.api.service.CommandService;
|
||||
import io.metersphere.base.domain.ApiDefinition;
|
||||
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
||||
|
@ -26,6 +29,7 @@ import io.metersphere.commons.constants.MsTestElementConstants;
|
|||
import io.metersphere.commons.constants.RunModeConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.ScriptEngineUtils;
|
||||
import io.metersphere.track.service.TestPlanApiCaseService;
|
||||
|
@ -34,6 +38,7 @@ import lombok.EqualsAndHashCode;
|
|||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.config.Arguments;
|
||||
import org.apache.jmeter.config.KeystoreConfig;
|
||||
import org.apache.jmeter.protocol.http.control.Header;
|
||||
import org.apache.jmeter.protocol.http.control.HeaderManager;
|
||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||
|
@ -110,6 +115,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
@JSONField(ordinal = 37)
|
||||
private Boolean isRefEnvironment;
|
||||
|
||||
@JSONField(ordinal = 38)
|
||||
private String alias;
|
||||
|
||||
private void setRefElement() {
|
||||
try {
|
||||
ApiDefinitionService apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class);
|
||||
|
@ -346,14 +354,63 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
MsDNSCacheManager.addEnvironmentVariables(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId()));
|
||||
MsDNSCacheManager.addEnvironmentDNS(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId()));
|
||||
}
|
||||
|
||||
if (this.authManager != null) {
|
||||
this.authManager.setAuth(tree, this.authManager, sampler);
|
||||
}
|
||||
|
||||
// 加载SSL认证
|
||||
if (config != null && config.isEffective(this.getProjectId()) && config.getConfig().get(this.getProjectId()).getSslConfig() != null) {
|
||||
if (CollectionUtils.isNotEmpty(config.getConfig().get(this.getProjectId()).getSslConfig().getFiles())) {
|
||||
MsKeyStore msKeyStore = config.getKeyStoreMap().get(this.getProjectId());
|
||||
CommandService commandService = CommonBeanFactory.getBean(CommandService.class);
|
||||
if (msKeyStore == null) {
|
||||
msKeyStore = new MsKeyStore();
|
||||
if (config.getConfig().get(this.getProjectId()).getSslConfig().getFiles().size() == 1) {
|
||||
// 加载认证文件
|
||||
KeyStoreFile file = config.getConfig().get(this.getProjectId()).getSslConfig().getFiles().get(0);
|
||||
msKeyStore.setPath(FileUtils.BODY_FILE_DIR + "/ssl/" + file.getId() + "_" + file.getName());
|
||||
msKeyStore.setPassword(file.getPassword());
|
||||
} else {
|
||||
// 合并多个认证文件
|
||||
msKeyStore.setPath(FileUtils.BODY_FILE_DIR + "/ssl/tmp." + this.getId() + ".jks");
|
||||
msKeyStore.setPassword("ms123...");
|
||||
commandService.mergeKeyStore(msKeyStore.getPath(), config.getConfig().get(this.getProjectId()).getSslConfig());
|
||||
}
|
||||
}
|
||||
if (StringUtils.isEmpty(this.alias)) {
|
||||
this.alias = config.getConfig().get(this.getProjectId()).getSslConfig().getDefaultAlias();
|
||||
}
|
||||
|
||||
if (StringUtils.isNotEmpty(this.alias)) {
|
||||
String aliasVar = UUID.randomUUID().toString();
|
||||
this.addArguments(httpSamplerTree, aliasVar, this.alias.trim());
|
||||
// 校验 keystore
|
||||
commandService.checkKeyStore(msKeyStore.getPassword(), msKeyStore.getPath());
|
||||
|
||||
KeystoreConfig keystoreConfig = new KeystoreConfig();
|
||||
keystoreConfig.setEnabled(true);
|
||||
keystoreConfig.setName(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "-KeyStore" : "KeyStore");
|
||||
keystoreConfig.setProperty(TestElement.TEST_CLASS, KeystoreConfig.class.getName());
|
||||
keystoreConfig.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
keystoreConfig.setProperty("clientCertAliasVarName", aliasVar);
|
||||
keystoreConfig.setProperty("endIndex", -1);
|
||||
keystoreConfig.setProperty("preload", true);
|
||||
keystoreConfig.setProperty("startIndex", 0);
|
||||
keystoreConfig.setProperty("MS-KEYSTORE-FILE-PATH", msKeyStore.getPath());
|
||||
keystoreConfig.setProperty("MS-KEYSTORE-FILE-PASSWORD", msKeyStore.getPassword());
|
||||
httpSamplerTree.add(keystoreConfig);
|
||||
|
||||
config.getKeyStoreMap().put(this.getProjectId(), new MsKeyStore(msKeyStore.getPath(), msKeyStore.getPassword()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||
for (MsTestElement el : hashTree) {
|
||||
el.toHashTree(httpSamplerTree, el.getHashTree(), config);
|
||||
}
|
||||
}
|
||||
if (this.authManager != null) {
|
||||
this.authManager.setAuth(tree, this.authManager, sampler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 兼容旧数据
|
||||
|
@ -586,6 +643,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
return null;
|
||||
}
|
||||
|
||||
private void addArguments(HashTree tree, String key, String value) {
|
||||
Arguments arguments = new Arguments();
|
||||
arguments.setEnabled(true);
|
||||
arguments.setName(StringUtils.isNotEmpty(this.getName()) ? this.getName() + "-KeyStoreAlias" : "KeyStoreAlias");
|
||||
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());
|
||||
arguments.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ArgumentsPanel"));
|
||||
arguments.addArgument(key, value, "=");
|
||||
tree.add(arguments);
|
||||
}
|
||||
|
||||
private boolean isRest() {
|
||||
return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.api.dto.scenario.environment;
|
|||
import io.metersphere.api.dto.scenario.DatabaseConfig;
|
||||
import io.metersphere.api.dto.scenario.HttpConfig;
|
||||
import io.metersphere.api.dto.scenario.TCPConfig;
|
||||
import io.metersphere.api.dto.ssl.KeyStoreConfig;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -14,11 +15,13 @@ public class EnvironmentConfig {
|
|||
private HttpConfig httpConfig;
|
||||
private List<DatabaseConfig> databaseConfigs;
|
||||
private TCPConfig tcpConfig;
|
||||
private KeyStoreConfig sslConfig;
|
||||
|
||||
public EnvironmentConfig() {
|
||||
this.commonConfig = new CommonConfig();
|
||||
this.httpConfig = new HttpConfig();
|
||||
this.databaseConfigs = new ArrayList<>();
|
||||
this.tcpConfig = new TCPConfig();
|
||||
this.sslConfig = new KeyStoreConfig();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package io.metersphere.api.dto.ssl;
|
||||
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
public class KeyStoreConfig {
|
||||
private List<KeyStoreEntry> entrys;
|
||||
private List<KeyStoreFile> files;
|
||||
|
||||
public String getDefaultAlias() {
|
||||
if (CollectionUtils.isNotEmpty(entrys)) {
|
||||
List<KeyStoreEntry> entryList = this.entrys.stream().filter(KeyStoreEntry::isDefault).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(entryList)) {
|
||||
if (StringUtils.isNotEmpty(entryList.get(0).getNewAsName())) {
|
||||
return entryList.get(0).getNewAsName();
|
||||
} else {
|
||||
return entryList.get(0).getOriginalAsName();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.api.dto.ssl;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class KeyStoreEntry {
|
||||
private String id;
|
||||
private String originalAsName;
|
||||
private String newAsName;
|
||||
private String type;
|
||||
private String password;
|
||||
private String sourceName;
|
||||
private String sourceId;
|
||||
private boolean isDefault;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.api.dto.ssl;
|
||||
|
||||
import io.metersphere.api.dto.scenario.request.BodyFile;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class KeyStoreFile {
|
||||
private String id;
|
||||
private String name;
|
||||
private String type;
|
||||
private String updateTime;
|
||||
private String password;
|
||||
private BodyFile file;
|
||||
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.api.dto.ssl;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MsKeyStore {
|
||||
private String id;
|
||||
private String password;
|
||||
private String path;
|
||||
|
||||
public MsKeyStore() {
|
||||
}
|
||||
|
||||
public MsKeyStore(String path, String password) {
|
||||
this.password = password;
|
||||
this.path = path;
|
||||
}
|
||||
}
|
|
@ -2,11 +2,13 @@ package io.metersphere.api.service;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.api.dto.ApiTestEnvironmentDTO;
|
||||
import io.metersphere.api.dto.mockconfig.MockConfigStaticData;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentExample;
|
||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.controller.request.EnvironmentRequest;
|
||||
import io.metersphere.dto.BaseSystemConfigDTO;
|
||||
|
@ -16,6 +18,7 @@ import org.apache.commons.collections.CollectionUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
|
@ -72,6 +75,19 @@ public class ApiTestEnvironmentService {
|
|||
return apiTestEnvironmentWithBLOBs.getId();
|
||||
}
|
||||
|
||||
public String add(ApiTestEnvironmentDTO request, List<MultipartFile> sslFiles) {
|
||||
request.setId(UUID.randomUUID().toString());
|
||||
checkEnvironmentExist(request);
|
||||
FileUtils.createFiles(request.getUploadIds(), sslFiles, FileUtils.BODY_FILE_DIR + "/ssl");
|
||||
apiTestEnvironmentMapper.insert(request);
|
||||
return request.getId();
|
||||
}
|
||||
|
||||
public void update(ApiTestEnvironmentDTO apiTestEnvironment,List<MultipartFile> sslFiles) {
|
||||
checkEnvironmentExist(apiTestEnvironment);
|
||||
FileUtils.createFiles(apiTestEnvironment.getUploadIds(), sslFiles, FileUtils.BODY_FILE_DIR + "/ssl");
|
||||
apiTestEnvironmentMapper.updateByPrimaryKeyWithBLOBs(apiTestEnvironment);
|
||||
}
|
||||
private void checkEnvironmentExist(ApiTestEnvironmentWithBLOBs environment) {
|
||||
if (environment.getName() != null) {
|
||||
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
|
||||
|
|
|
@ -0,0 +1,213 @@
|
|||
package io.metersphere.api.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.api.dto.ssl.KeyStoreConfig;
|
||||
import io.metersphere.api.dto.ssl.KeyStoreEntry;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.FileUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jorphan.exec.SystemCommand;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
public class CommandService {
|
||||
|
||||
public List<KeyStoreEntry> get(String password, MultipartFile file) {
|
||||
try {
|
||||
String path = FileUtils.createFile(file);
|
||||
// 执行验证指令
|
||||
if (StringUtils.isNotEmpty(password)) {
|
||||
password = JSON.parseObject(password, String.class);
|
||||
}
|
||||
String keytoolArgs[] = {"keytool", "-rfc", "-list", "-keystore", path, "-storepass", password};
|
||||
Process p = new ProcessBuilder(keytoolArgs).start();
|
||||
List<KeyStoreEntry> dtoList = new LinkedList<>();
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
|
||||
String line = null;
|
||||
KeyStoreEntry dto = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.contains("keystore password was incorrect")) {
|
||||
MSException.throwException("认证密码错误,请重新输入密码");
|
||||
}
|
||||
if (line.startsWith("别名")) {
|
||||
if (dto != null) {
|
||||
dtoList.add(dto);
|
||||
}
|
||||
dto = new KeyStoreEntry();
|
||||
dto.setOriginalAsName(line.split(":")[1]);
|
||||
}
|
||||
if (line.startsWith("条目类型")) {
|
||||
dto.setType(line.split(":")[1]);
|
||||
}
|
||||
}
|
||||
if (dto != null) {
|
||||
dtoList.add(dto);
|
||||
}
|
||||
}
|
||||
FileUtils.deleteFile(path);
|
||||
return dtoList;
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void createKeyStore(String alias, String path) {
|
||||
try {
|
||||
File f = new File(path);
|
||||
if (f.exists()) {
|
||||
f.delete();
|
||||
}
|
||||
List<String> arguments = new ArrayList();
|
||||
arguments.add("keytool");
|
||||
arguments.add("-genkeypair");
|
||||
arguments.add("-alias");
|
||||
arguments.add(alias);
|
||||
arguments.add("-dname");
|
||||
arguments.add("CN=localhost,OU=cn,O=cn,L=cn,ST=cn,C=cn");
|
||||
arguments.add("-keyalg");
|
||||
arguments.add("RSA");
|
||||
arguments.add("-keystore");
|
||||
arguments.add(f.getName());
|
||||
arguments.add("-storepass");
|
||||
arguments.add("ms123...");
|
||||
arguments.add("-keypass");
|
||||
arguments.add("ms123...");
|
||||
arguments.add("-validity");
|
||||
arguments.add(Integer.toString(1024));
|
||||
SystemCommand nativeCommand = new SystemCommand(f.getParentFile(), (Map) null);
|
||||
nativeCommand.run(arguments);
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void mergeKeyStore(String newKeyStore, KeyStoreConfig sslConfig) {
|
||||
try {
|
||||
// 创建零时keyStore
|
||||
this.createKeyStore("ms-run", newKeyStore);
|
||||
// 修改别名
|
||||
Map<String, List<KeyStoreEntry>> entryMap = new HashMap<>();
|
||||
if (sslConfig != null && CollectionUtils.isNotEmpty(sslConfig.getEntrys())) {
|
||||
sslConfig.getEntrys().forEach(item -> {
|
||||
if (entryMap.containsKey(item.getSourceId())) {
|
||||
entryMap.get(item.getSourceId()).add(item);
|
||||
} else {
|
||||
List<KeyStoreEntry> list = new ArrayList<>();
|
||||
list.add(item);
|
||||
entryMap.put(item.getSourceId(), list);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (sslConfig != null && CollectionUtils.isNotEmpty(sslConfig.getFiles())) {
|
||||
sslConfig.getFiles().forEach(item -> {
|
||||
List<KeyStoreEntry> entries = entryMap.get(item.getId());
|
||||
if (CollectionUtils.isNotEmpty(entries)) {
|
||||
entries.forEach(entry -> {
|
||||
File srcFile = new File(FileUtils.BODY_FILE_DIR + "/ssl/" + item.getId() + "_" + item.getName());
|
||||
try {
|
||||
// 开始合并
|
||||
File destFile = new File(newKeyStore);
|
||||
List<String> arguments = new ArrayList();
|
||||
arguments.add("keytool");
|
||||
arguments.add("-genkeypair");
|
||||
arguments.add("-importkeystore");
|
||||
arguments.add("-srckeystore");
|
||||
arguments.add(srcFile.getName());
|
||||
arguments.add("-srcstorepass");
|
||||
arguments.add(item.getPassword());
|
||||
arguments.add("-srcalias");
|
||||
arguments.add(entry.getOriginalAsName().trim());
|
||||
arguments.add("-srckeypass");
|
||||
arguments.add(entry.getPassword());
|
||||
|
||||
arguments.add("-destkeystore");
|
||||
arguments.add(destFile.getName());
|
||||
arguments.add("-deststorepass");
|
||||
arguments.add("ms123...");
|
||||
arguments.add("-destalias");
|
||||
arguments.add(StringUtils.isNotEmpty(entry.getNewAsName()) ? entry.getNewAsName().trim() : entry.getOriginalAsName().trim());
|
||||
arguments.add("-destkeypass");
|
||||
arguments.add("ms123...");
|
||||
|
||||
SystemCommand nativeCommand = new SystemCommand(destFile.getParentFile(), (Map) null);
|
||||
int exitVal = nativeCommand.run(arguments);
|
||||
if (exitVal > 0) {
|
||||
MSException.throwException("合并条目:【" + entry.getOriginalAsName() + " 】失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void keypasswd(File file, String storepass, String alias, String keypass) {
|
||||
// 统一密码
|
||||
try {
|
||||
List<String> arguments = new ArrayList();
|
||||
arguments.add("keytool");
|
||||
arguments.add("-genkeypair");
|
||||
arguments.add("-keypasswd");
|
||||
arguments.add("-keystore");
|
||||
arguments.add(file.getName());
|
||||
arguments.add("-storepass");
|
||||
arguments.add(storepass);
|
||||
arguments.add("-alias");
|
||||
arguments.add(alias.trim());
|
||||
|
||||
arguments.add("-keypass");
|
||||
arguments.add(keypass);
|
||||
arguments.add("-new");
|
||||
arguments.add("ms123...");
|
||||
SystemCommand nativeCommand = new SystemCommand(file.getParentFile(), (Map) null);
|
||||
int exitVal = nativeCommand.run(arguments);
|
||||
if (exitVal > 0) {
|
||||
MSException.throwException("别名:【" + alias + " 】密码修改失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean checkKeyStore(String password, String path) {
|
||||
try {
|
||||
String keytoolArgs[] = {"keytool", "-rfc", "-list", "-keystore", path, "-storepass", password};
|
||||
Process p = new ProcessBuilder(keytoolArgs).start();
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
|
||||
String line = null;
|
||||
KeyStoreEntry dto = null;
|
||||
while ((line = br.readLine()) != null) {
|
||||
if (line.contains("keystore password was incorrect")) {
|
||||
MSException.throwException("认证密码错误,请重新输入密码");
|
||||
}
|
||||
if (line.contains("Exception")) {
|
||||
MSException.throwException("认证文件加载失败,请检查认证文件");
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
MSException.throwException(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,27 +3,37 @@ package io.metersphere.commons.utils;
|
|||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.util.FileUtil;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FileUtils {
|
||||
public static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
|
||||
|
||||
public static void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
|
||||
private static void create(List<String> bodyUploadIds, List<MultipartFile> bodyFiles, String path) {
|
||||
String filePath = BODY_FILE_DIR;
|
||||
if (StringUtils.isNotEmpty(path)) {
|
||||
filePath = path;
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(bodyUploadIds) && CollectionUtils.isNotEmpty(bodyFiles)) {
|
||||
File testDir = new File(BODY_FILE_DIR);
|
||||
File testDir = new File(filePath);
|
||||
if (!testDir.exists()) {
|
||||
testDir.mkdirs();
|
||||
}
|
||||
for (int i = 0; i < bodyUploadIds.size(); i++) {
|
||||
MultipartFile item = bodyFiles.get(i);
|
||||
File file = new File(BODY_FILE_DIR + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
|
||||
File file = new File(filePath + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
|
||||
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
|
||||
file.createNewFile();
|
||||
FileUtil.copyStream(in, out);
|
||||
final int MAX = 4096;
|
||||
byte[] buf = new byte[MAX];
|
||||
for (int bytesRead = in.read(buf, 0, MAX); bytesRead != -1; bytesRead = in.read(buf, 0, MAX)) {
|
||||
out.write(buf, 0, bytesRead);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e);
|
||||
MSException.throwException(Translator.get("upload_fail"));
|
||||
|
@ -32,6 +42,33 @@ public class FileUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
|
||||
FileUtils.create(bodyUploadIds, bodyFiles, null);
|
||||
}
|
||||
|
||||
public static void createFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles, String path) {
|
||||
FileUtils.create(bodyUploadIds, bodyFiles, path);
|
||||
}
|
||||
|
||||
public static String createFile(MultipartFile bodyFile) {
|
||||
File file = new File("/opt/metersphere/data/body/tmp" + UUID.randomUUID().toString() + "_" + bodyFile.getOriginalFilename());
|
||||
try (InputStream in = bodyFile.getInputStream(); OutputStream out = new FileOutputStream(file)) {
|
||||
file.createNewFile();
|
||||
FileUtil.copyStream(in, out);
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e);
|
||||
MSException.throwException(Translator.get("upload_fail"));
|
||||
}
|
||||
return file.getPath();
|
||||
}
|
||||
|
||||
public static void delFile(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static String uploadFile(MultipartFile uploadFile, String path, String name) {
|
||||
if (uploadFile == null) {
|
||||
return null;
|
||||
|
@ -53,7 +90,7 @@ public class FileUtils {
|
|||
}
|
||||
|
||||
public static String uploadFile(MultipartFile uploadFile, String path) {
|
||||
return uploadFile(uploadFile, path, uploadFile.getOriginalFilename());
|
||||
return uploadFile(uploadFile, path, uploadFile.getOriginalFilename());
|
||||
}
|
||||
|
||||
public static void deleteFile(String path) {
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.jmeter.config;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.gui.TestElementMetadata;
|
||||
import org.apache.jmeter.testbeans.TestBean;
|
||||
import org.apache.jmeter.testelement.TestStateListener;
|
||||
import org.apache.jmeter.util.JMeterUtils;
|
||||
import org.apache.jmeter.util.SSLManager;
|
||||
import org.apache.jorphan.util.JMeterStopTestException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Configure Keystore
|
||||
*/
|
||||
@TestElementMetadata(labelResource = "displayName")
|
||||
public class KeystoreConfig extends ConfigTestElement implements TestBean, TestStateListener {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final Logger log = LoggerFactory.getLogger(KeystoreConfig.class);
|
||||
|
||||
private static final String KEY_STORE_START_INDEX = "https.keyStoreStartIndex"; // $NON-NLS-1$
|
||||
private static final String KEY_STORE_END_INDEX = "https.keyStoreEndIndex"; // $NON-NLS-1$
|
||||
|
||||
private String startIndex;
|
||||
private String endIndex;
|
||||
private String preload;
|
||||
private String clientCertAliasVarName;
|
||||
|
||||
public KeystoreConfig() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testEnded() {
|
||||
testEnded(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testEnded(String host) {
|
||||
log.info("Destroying Keystore");
|
||||
SSLManager.getInstance().destroyKeystore();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted() {
|
||||
testStarted(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted(String host) {
|
||||
String reuseSSLContext = JMeterUtils.getProperty("https.use.cached.ssl.context");
|
||||
if (StringUtils.isEmpty(reuseSSLContext) || "true".equals(reuseSSLContext)) {
|
||||
log.warn("https.use.cached.ssl.context property must be set to false to ensure Multiple Certificates are used");
|
||||
}
|
||||
int startIndexAsInt = JMeterUtils.getPropDefault(KEY_STORE_START_INDEX, 0);
|
||||
int endIndexAsInt = JMeterUtils.getPropDefault(KEY_STORE_END_INDEX, -1);
|
||||
|
||||
if (!StringUtils.isEmpty(this.startIndex)) {
|
||||
try {
|
||||
startIndexAsInt = Integer.parseInt(this.startIndex);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Failed parsing startIndex: {}, will default to: {}, error message: {}", this.startIndex,
|
||||
startIndexAsInt, e, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(this.endIndex)) {
|
||||
try {
|
||||
endIndexAsInt = Integer.parseInt(this.endIndex);
|
||||
} catch (NumberFormatException e) {
|
||||
log.warn("Failed parsing endIndex: {}, will default to: {}, error message: {}", this.endIndex,
|
||||
endIndexAsInt, e, e);
|
||||
}
|
||||
}
|
||||
if (endIndexAsInt != -1 && startIndexAsInt > endIndexAsInt) {
|
||||
throw new JMeterStopTestException("Keystore Config error : Alias start index must be lower than Alias end index");
|
||||
}
|
||||
log.info(
|
||||
"Configuring Keystore with (preload: '{}', startIndex: {}, endIndex: {}, clientCertAliasVarName: '{}')",
|
||||
preload, startIndexAsInt, endIndexAsInt, clientCertAliasVarName);
|
||||
// 加载认证文件
|
||||
String path = this.getPropertyAsString("MS-KEYSTORE-FILE-PATH");
|
||||
String password = this.getPropertyAsString("MS-KEYSTORE-FILE-PASSWORD");
|
||||
InputStream in = null;
|
||||
try {
|
||||
in = new FileInputStream(new File(path));
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
SSLManager.getInstance().configureKeystore(Boolean.parseBoolean(preload),
|
||||
startIndexAsInt,
|
||||
endIndexAsInt,
|
||||
clientCertAliasVarName, in, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the endIndex
|
||||
*/
|
||||
public String getEndIndex() {
|
||||
return endIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param endIndex the endIndex to set
|
||||
*/
|
||||
public void setEndIndex(String endIndex) {
|
||||
this.endIndex = endIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the startIndex
|
||||
*/
|
||||
public String getStartIndex() {
|
||||
return startIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param startIndex the startIndex to set
|
||||
*/
|
||||
public void setStartIndex(String startIndex) {
|
||||
this.startIndex = startIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the preload
|
||||
*/
|
||||
public String getPreload() {
|
||||
return preload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param preload the preload to set
|
||||
*/
|
||||
public void setPreload(String preload) {
|
||||
this.preload = preload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clientCertAliasVarName
|
||||
*/
|
||||
public String getClientCertAliasVarName() {
|
||||
return clientCertAliasVarName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientCertAliasVarName the clientCertAliasVarName to set
|
||||
*/
|
||||
public void setClientCertAliasVarName(String clientCertAliasVarName) {
|
||||
this.clientCertAliasVarName = clientCertAliasVarName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to you under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.jmeter.util;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.jmeter.gui.GuiPackage;
|
||||
import org.apache.jmeter.util.keystore.JmeterKeyStore;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* The SSLManager handles the KeyStore information for JMeter. Basically, it
|
||||
* handles all the logic for loading and initializing all the JSSE parameters
|
||||
* and selecting the alias to authenticate against if it is available.
|
||||
* SSLManager will try to automatically select the client certificate for you,
|
||||
* but if it can't make a decision, it will pop open a dialog asking you for
|
||||
* more information.
|
||||
* <p>
|
||||
* TODO? - N.B. does not currently allow the selection of a client certificate.
|
||||
*
|
||||
*/
|
||||
public abstract class SSLManager {
|
||||
private static final Logger log = LoggerFactory.getLogger(SSLManager.class);
|
||||
|
||||
private static final String SSL_TRUST_STORE = "javax.net.ssl.trustStore";// $NON-NLS-1$
|
||||
|
||||
private static final String KEY_STORE_PASSWORD = "javax.net.ssl.keyStorePassword"; // $NON-NLS-1$ NOSONAR no hard coded password
|
||||
|
||||
public static final String JAVAX_NET_SSL_KEY_STORE = "javax.net.ssl.keyStore"; // $NON-NLS-1$
|
||||
|
||||
private static final String JAVAX_NET_SSL_KEY_STORE_TYPE = "javax.net.ssl.keyStoreType"; // $NON-NLS-1$
|
||||
|
||||
private static final String PKCS12 = "pkcs12"; // $NON-NLS-1$
|
||||
|
||||
/** Singleton instance of the manager */
|
||||
private static SSLManager manager;
|
||||
|
||||
private static final boolean IS_SSL_SUPPORTED = true;
|
||||
|
||||
/** Cache the KeyStore instance */
|
||||
private JmeterKeyStore keyStore;
|
||||
|
||||
/** Cache the TrustStore instance - null if no truststore name was provided */
|
||||
private KeyStore trustStore = null;
|
||||
// Have we yet tried to load the truststore?
|
||||
private volatile boolean truststoreLoaded=false;
|
||||
|
||||
/** Have the password available */
|
||||
protected volatile String defaultpw = System.getProperty(KEY_STORE_PASSWORD);
|
||||
|
||||
private int keystoreAliasStartIndex;
|
||||
|
||||
private int keystoreAliasEndIndex;
|
||||
|
||||
private String clientCertAliasVarName;
|
||||
|
||||
/**
|
||||
* Resets the SSLManager so that we can create a new one with a new keystore
|
||||
*/
|
||||
public static synchronized void reset() {
|
||||
SSLManager.manager = null;
|
||||
}
|
||||
|
||||
public abstract void setContext(HttpURLConnection conn);
|
||||
|
||||
/**
|
||||
* Default implementation of setting the Provider
|
||||
*
|
||||
* @param provider
|
||||
* the provider to use
|
||||
*/
|
||||
protected void setProvider(Provider provider) {
|
||||
if (null != provider) {
|
||||
Security.addProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
protected synchronized JmeterKeyStore getKeyStore() {
|
||||
if (null == this.keyStore) {
|
||||
String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE,""); // empty if not provided
|
||||
String fileType = System.getProperty(JAVAX_NET_SSL_KEY_STORE_TYPE, // use the system property to determine the type
|
||||
fileName.toLowerCase(Locale.ENGLISH).endsWith(".p12") ? PKCS12 : "JKS"); // otherwise use the name
|
||||
log.info("JmeterKeyStore Location: {} type {}", fileName, fileType);
|
||||
try {
|
||||
this.keyStore = JmeterKeyStore.getInstance(fileType, keystoreAliasStartIndex, keystoreAliasEndIndex, clientCertAliasVarName);
|
||||
log.info("KeyStore created OK");
|
||||
} catch (Exception e) {
|
||||
this.keyStore = null;
|
||||
throw new IllegalArgumentException("Could not create keystore: "+e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// The string 'NONE' is used for the keystore location when using PKCS11
|
||||
// https://docs.oracle.com/javase/8/docs/technotes/guides/security/p11guide.html#JSSE
|
||||
if ("NONE".equalsIgnoreCase(fileName)) {
|
||||
retryLoadKeys(null, false);
|
||||
log.info("Total of {} aliases loaded OK from PKCS11", keyStore.getAliasCount());
|
||||
} else {
|
||||
File initStore = new File(fileName);
|
||||
if (fileName.length() > 0 && initStore.exists()) {
|
||||
retryLoadKeys(initStore, true);
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Total of {} aliases loaded OK from keystore {}",
|
||||
keyStore.getAliasCount(), fileName);
|
||||
}
|
||||
} else {
|
||||
log.warn("Keystore file not found, loading empty keystore");
|
||||
this.defaultpw = ""; // Ensure not null
|
||||
this.keyStore.load(null, "");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Problem loading keystore: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JmeterKeyStore type: {}", this.keyStore.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
return this.keyStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and initializes the KeyStore. If the password for the KeyStore is
|
||||
* not set, this method will prompt you to enter it. Unfortunately, there is
|
||||
* no PasswordEntryField available from JOptionPane.
|
||||
*
|
||||
* @return the configured {@link JmeterKeyStore}
|
||||
*/
|
||||
protected synchronized JmeterKeyStore getKeyStore(InputStream is,String password) {
|
||||
if (null == this.keyStore) {
|
||||
String fileName = System.getProperty(JAVAX_NET_SSL_KEY_STORE,""); // empty if not provided
|
||||
String fileType = System.getProperty(JAVAX_NET_SSL_KEY_STORE_TYPE, // use the system property to determine the type
|
||||
fileName.toLowerCase(Locale.ENGLISH).endsWith(".p12") ? PKCS12 : "JKS"); // otherwise use the name
|
||||
log.info("JmeterKeyStore Location: {} type {}", fileName, fileType);
|
||||
try {
|
||||
this.keyStore = JmeterKeyStore.getInstance(fileType, keystoreAliasStartIndex, keystoreAliasEndIndex, clientCertAliasVarName);
|
||||
log.info("KeyStore created OK");
|
||||
} catch (Exception e) {
|
||||
this.keyStore = null;
|
||||
throw new IllegalArgumentException("Could not create keystore: "+e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
// The string 'NONE' is used for the keystore location when using PKCS11
|
||||
// https://docs.oracle.com/javase/8/docs/technotes/guides/security/p11guide.html#JSSE
|
||||
if ("NONE".equalsIgnoreCase(fileName)) {
|
||||
retryLoadKeys(null, false);
|
||||
log.info("Total of {} aliases loaded OK from PKCS11", keyStore.getAliasCount());
|
||||
} else {
|
||||
File initStore = new File(fileName);
|
||||
if (fileName.length() > 0 && initStore.exists()) {
|
||||
retryLoadKeys(initStore, true);
|
||||
if (log.isInfoEnabled()) {
|
||||
log.info("Total of {} aliases loaded OK from keystore {}",
|
||||
keyStore.getAliasCount(), fileName);
|
||||
}
|
||||
} else {
|
||||
log.warn("Keystore file not found, loading empty keystore");
|
||||
this.defaultpw = ""; // Ensure not null
|
||||
this.keyStore.load(is, password);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("Problem loading keystore: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JmeterKeyStore type: {}", this.keyStore.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
return this.keyStore;
|
||||
}
|
||||
|
||||
private void retryLoadKeys(File initStore, boolean allowEmptyPassword) throws NoSuchAlgorithmException,
|
||||
CertificateException, IOException, KeyStoreException, UnrecoverableKeyException {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
String password = getPassword();
|
||||
if (!allowEmptyPassword) {
|
||||
Validate.notNull(password, "Password for keystore must not be null");
|
||||
}
|
||||
try {
|
||||
if (initStore == null) {
|
||||
this.keyStore.load(null, password);
|
||||
} else {
|
||||
try (InputStream fis = new FileInputStream(initStore)) {
|
||||
this.keyStore.load(fis, password);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
log.debug("Could not load keystore. Wrong password for keystore?", e);
|
||||
}
|
||||
this.defaultpw = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The password can be defined as a property; this dialogue is provided to allow it
|
||||
* to be entered at run-time.
|
||||
*/
|
||||
private String getPassword() {
|
||||
String password = this.defaultpw;
|
||||
if (null == password) {
|
||||
final GuiPackage guiInstance = GuiPackage.getInstance();
|
||||
// if (guiInstance != null) {
|
||||
// JPanel panel = new JPanel(new MigLayout("fillx, wrap 2", "[][fill, grow]"));
|
||||
// JLabel passwordLabel = new JLabel("Password: ");
|
||||
// JPasswordField pwf = new JPasswordField(64);
|
||||
// pwf.setEchoChar('*');
|
||||
// passwordLabel.setLabelFor(pwf);
|
||||
// panel.add(passwordLabel);
|
||||
// panel.add(pwf);
|
||||
// int choice = JOptionPane.showConfirmDialog(guiInstance.getMainFrame(), panel,
|
||||
// JMeterUtils.getResString("ssl_pass_prompt"), JOptionPane.OK_CANCEL_OPTION,
|
||||
// JOptionPane.PLAIN_MESSAGE);
|
||||
// if (choice == JOptionPane.OK_OPTION) {
|
||||
// char[] pwchars = pwf.getPassword();
|
||||
// this.defaultpw = new String(pwchars);
|
||||
// Arrays.fill(pwchars, '*');
|
||||
// }
|
||||
// System.setProperty(KEY_STORE_PASSWORD, this.defaultpw);
|
||||
// password = this.defaultpw;
|
||||
// }
|
||||
} else {
|
||||
log.warn("No password provided, and no GUI present so cannot prompt");
|
||||
}
|
||||
return password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens and initializes the TrustStore.
|
||||
*
|
||||
* There are 3 possibilities:
|
||||
* <ul>
|
||||
* <li>no truststore name provided, in which case the default Java truststore
|
||||
* should be used</li>
|
||||
* <li>truststore name is provided, and loads OK</li>
|
||||
* <li>truststore name is provided, but is not found or does not load OK, in
|
||||
* which case an empty
|
||||
* truststore is created</li>
|
||||
* </ul>
|
||||
* If the KeyStore object cannot be created, then this is currently treated the
|
||||
* same as if no truststore name was provided.
|
||||
*
|
||||
* @return
|
||||
* {@code null} when Java truststore should be used.
|
||||
* Otherwise the truststore, which may be empty if the file could not be
|
||||
* loaded.
|
||||
*
|
||||
*/
|
||||
protected KeyStore getTrustStore() {
|
||||
if (!truststoreLoaded) {
|
||||
|
||||
truststoreLoaded=true;// we've tried ...
|
||||
|
||||
String fileName = System.getProperty(SSL_TRUST_STORE);
|
||||
if (fileName == null) {
|
||||
return null;
|
||||
}
|
||||
log.info("TrustStore Location: {}", fileName);
|
||||
|
||||
try {
|
||||
this.trustStore = KeyStore.getInstance("JKS");
|
||||
log.info("TrustStore created OK, Type: JKS");
|
||||
} catch (Exception e) {
|
||||
this.trustStore = null;
|
||||
throw new RuntimeException("Problem creating truststore: "+e.getMessage(), e);
|
||||
}
|
||||
|
||||
try {
|
||||
File initStore = new File(fileName);
|
||||
|
||||
if (initStore.exists()) {
|
||||
try (InputStream fis = new FileInputStream(initStore)) {
|
||||
this.trustStore.load(fis, null);
|
||||
log.info("Truststore loaded OK from file");
|
||||
}
|
||||
} else {
|
||||
log.warn("Truststore file not found, loading empty truststore");
|
||||
this.trustStore.load(null, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Can't load TrustStore: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
return this.trustStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected Constructor to remove the possibility of directly instantiating
|
||||
* this object. Create the SSLContext, and wrap all the X509KeyManagers with
|
||||
* our X509KeyManager so that we can choose our alias.
|
||||
*/
|
||||
protected SSLManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Static accessor for the SSLManager object. The SSLManager is a singleton.
|
||||
*
|
||||
* @return the singleton {@link SSLManager}
|
||||
*/
|
||||
public static synchronized SSLManager getInstance() {
|
||||
if (null == SSLManager.manager) {
|
||||
SSLManager.manager = new JsseSSLManager(null);
|
||||
}
|
||||
|
||||
return SSLManager.manager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether SSL is supported or not.
|
||||
*
|
||||
* @return flag whether SSL is supported
|
||||
*/
|
||||
public static boolean isSSLSupported() {
|
||||
return SSLManager.IS_SSL_SUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure Keystore
|
||||
*
|
||||
* @param preload
|
||||
* flag whether the keystore should be opened within this method,
|
||||
* or the opening should be delayed
|
||||
* @param startIndex
|
||||
* first index to consider for a key
|
||||
* @param endIndex
|
||||
* last index to consider for a key
|
||||
* @param clientCertAliasVarName
|
||||
* name of the default key, if empty the first key will be used
|
||||
* as default key
|
||||
*/
|
||||
public synchronized void configureKeystore(boolean preload, int startIndex, int endIndex, String clientCertAliasVarName,InputStream is,String password) {
|
||||
this.keystoreAliasStartIndex = startIndex;
|
||||
this.keystoreAliasEndIndex = endIndex;
|
||||
this.clientCertAliasVarName = clientCertAliasVarName;
|
||||
if(preload) {
|
||||
keyStore = getKeyStore(is,password);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy Keystore
|
||||
*/
|
||||
public synchronized void destroyKeystore() {
|
||||
keyStore=null;
|
||||
}
|
||||
}
|
|
@ -14,6 +14,14 @@
|
|||
<el-input-number size="small" :disabled="isReadOnly" v-model="request.responseTimeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row style="margin: 20px">
|
||||
<span style="margin-right: 10px">
|
||||
认证别名:
|
||||
</span>
|
||||
<span style="margin-right: 10px">
|
||||
<el-input size="small" style="width: 350px" v-model="request.alias"/>
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row style="margin: 20px">
|
||||
<span style="margin-right: 10px">
|
||||
<el-checkbox class="follow-redirects-item" v-model="request.followRedirects">{{$t('api_test.request.follow_redirects')}}</el-checkbox>
|
||||
|
|
|
@ -755,6 +755,7 @@ export class KeyValue extends BaseConfig {
|
|||
this.files = undefined;
|
||||
this.enable = undefined;
|
||||
this.uuid = undefined;
|
||||
this.time = undefined;
|
||||
this.contentType = undefined;
|
||||
this.set(options);
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
<el-tab-pane :label="$t('api_test.environment.tcp_config')" name="tcp">
|
||||
<ms-tcp-config :config="environment.config.tcpConfig"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('commons.ssl.config')" name="ssl">
|
||||
<ms-environment-s-s-l-config :project-id="projectId" :ssl-config="environment.config.sslConfig"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="environment-footer">
|
||||
|
@ -44,7 +47,10 @@
|
|||
import MsDatabaseConfig from "../request/database/DatabaseConfig";
|
||||
import MsEnvironmentHttpConfig from "./EnvironmentHttpConfig";
|
||||
import MsEnvironmentCommonConfig from "./EnvironmentCommonConfig";
|
||||
import MsEnvironmentSSLConfig from "./EnvironmentSSLConfig";
|
||||
|
||||
import MsTcpConfig from "@/business/components/api/test/components/request/tcp/TcpConfig";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "EnvironmentEdit",
|
||||
|
@ -52,6 +58,7 @@
|
|||
MsTcpConfig,
|
||||
MsEnvironmentCommonConfig,
|
||||
MsEnvironmentHttpConfig,
|
||||
MsEnvironmentSSLConfig,
|
||||
MsDatabaseConfig, MsApiHostTable, MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables
|
||||
},
|
||||
props: {
|
||||
|
@ -97,13 +104,32 @@
|
|||
});
|
||||
return isValidate;
|
||||
},
|
||||
geFiles(obj) {
|
||||
let uploadFiles = [];
|
||||
obj.uploadIds = [];
|
||||
if (obj.config && obj.config.sslConfig && obj.config.sslConfig.files) {
|
||||
obj.config.sslConfig.files.forEach(item => {
|
||||
if (item.file && item.file.size > 0) {
|
||||
if (!item.id) {
|
||||
item.name = item.file.name;
|
||||
item.id = getUUID();
|
||||
}
|
||||
obj.uploadIds.push(item.id);
|
||||
uploadFiles.push(item.file);
|
||||
}
|
||||
})
|
||||
}
|
||||
return uploadFiles;
|
||||
},
|
||||
_save(environment) {
|
||||
let bodyFiles = this.geFiles(environment);
|
||||
let param = this.buildParam(environment);
|
||||
let url = '/api/environment/add';
|
||||
if (param.id) {
|
||||
url = '/api/environment/update';
|
||||
}
|
||||
this.result = this.$post(url, param, response => {
|
||||
this.$fileUpload(url, null, bodyFiles, param, response => {
|
||||
//this.result = this.$post(url, param, response => {
|
||||
if (!param.id) {
|
||||
environment.id = response.data;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
<template>
|
||||
|
||||
<div>
|
||||
<div style="float: right;">
|
||||
<el-button size="mini" @click="open">{{$t('test_track.case.import.click_upload')}}</el-button>
|
||||
</div>
|
||||
<div class="tip">{{ this.$t('commons.ssl.files') }}
|
||||
</div>
|
||||
|
||||
<div class="ms-border">
|
||||
<el-table :data="sslConfig.files" highlight-current-row>
|
||||
<el-table-column prop="name" :label="$t('load_test.file_name')" show-overflow-tooltip width="180"/>
|
||||
<el-table-column prop="type" :label="$t('api_test.definition.request.esb_table.type')" show-overflow-tooltip min-width="100px"/>
|
||||
<el-table-column prop="password" show-overflow-tooltip min-width="120px" :label="$t('commons.password')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-input size="small" v-model="row.password" clearable show-password/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="updateTime" show-overflow-tooltip min-width="120px" :label="$t('load_test.last_modify_time')">
|
||||
<template v-slot:default="{row}">
|
||||
<span>{{ row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.operating')" width="100px">
|
||||
<template v-slot:default="{row}">
|
||||
<ms-table-operator-button :tip="$t('commons.update')" icon="el-icon-edit"
|
||||
type="primary" @exec="edit(row)"/>
|
||||
<ms-table-operator-button :tip="$t('api_test.automation.remove')"
|
||||
icon="el-icon-delete" @exec="remove(row)" type="danger" v-tester/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<p class="tip">{{ this.$t('commons.ssl.entry') }} </p>
|
||||
<div class="ms-border">
|
||||
<el-table :data="sslConfig.entrys" highlight-current-row v-if="!loading">
|
||||
<el-table-column prop="originalAsName" :label="$t('commons.ssl.original_as_name')" show-overflow-tooltip width="180"/>
|
||||
<el-table-column prop="newAsName" :label="$t('commons.ssl.new_as_name')" show-overflow-tooltip min-width="100px">
|
||||
<template v-slot:default="{row}">
|
||||
<el-input size="mini" v-model="row.newAsName"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" show-overflow-tooltip min-width="120px" :label="$t('api_test.definition.request.esb_table.type')"/>
|
||||
<el-table-column prop="password" show-overflow-tooltip min-width="120px" :label="$t('commons.password')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-input size="mini" v-model="row.password" show-password></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="sourceName" show-overflow-tooltip min-width="120px" :label="$t('commons.ssl.source')"/>
|
||||
<el-table-column :label="$t('commons.ssl.default')" width="100px">
|
||||
<template v-slot:default="{row}">
|
||||
<el-checkbox v-model="row.default" @change="changeCheck(row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<ms-s-s-l-file-upload :config="fileConfig" :sslConfig="sslConfig" :callback="addConfig" ref="sslConfigUpload"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {SSLConfig} from "../../model/EnvironmentModel";
|
||||
import MsApiKeyValue from "../ApiKeyValue";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsSelectTree from "../../../../common/select-tree/SelectTree";
|
||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import MsSSLFileUpload from "./SSLFileUpload";
|
||||
|
||||
export default {
|
||||
name: "MsEnvironmentSSLConfig",
|
||||
components: {MsApiKeyValue, MsSelectTree, MsTableOperatorButton, MsSSLFileUpload},
|
||||
props: {
|
||||
sslConfig: new SSLConfig(),
|
||||
projectId: String,
|
||||
},
|
||||
created() {
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
fileConfig: {},
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
projectId() {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.$refs.sslConfigUpload.open();
|
||||
},
|
||||
addConfig(config, file) {
|
||||
let sslFile = {id: config.id, name: file.name, type: file.type, updateTime: new Date().getTime(), password: config.password, file: file};
|
||||
if (!sslFile.type && sslFile.name) {
|
||||
let type = sslFile.name.substr(sslFile.name.lastIndexOf(".") + 1);
|
||||
sslFile.type = type;
|
||||
}
|
||||
if (file.size > 0) {
|
||||
this.getEntry(sslFile);
|
||||
}
|
||||
},
|
||||
edit(row) {
|
||||
this.$refs.sslConfigUpload.open(row);
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
});
|
||||
},
|
||||
getEntry(sslFile) {
|
||||
let url = '/api/environment/get/entry';
|
||||
this.$fileUpload(url, sslFile.file, null, sslFile.password, response => {
|
||||
let data = response.data;
|
||||
if (data) {
|
||||
if (!sslFile.id) {
|
||||
sslFile.id = getUUID();
|
||||
data.forEach(item => {
|
||||
if (item) {
|
||||
item.id = getUUID();
|
||||
item.sourceId = sslFile.id;
|
||||
item.sourceName = sslFile.name;
|
||||
}
|
||||
item.password = "";
|
||||
item.default = false;
|
||||
|
||||
this.sslConfig.entrys.unshift(item);
|
||||
})
|
||||
this.sslConfig.files.unshift(sslFile);
|
||||
} else {
|
||||
// 更新条目
|
||||
this.remove(sslFile);
|
||||
data.forEach(item => {
|
||||
if (item) {
|
||||
item.id = getUUID();
|
||||
item.sourceId = sslFile.id;
|
||||
item.sourceName = sslFile.name;
|
||||
}
|
||||
item.password = "";
|
||||
item.default = false;
|
||||
this.sslConfig.entrys.unshift(item);
|
||||
})
|
||||
this.sslConfig.files.unshift(sslFile);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
remove(row) {
|
||||
const index = this.sslConfig.files.findIndex((d) => d.id === row.id);
|
||||
this.sslConfig.files.splice(index, 1);
|
||||
// 同时删除条目
|
||||
if (this.sslConfig.entrys) {
|
||||
let removeKeys = [];
|
||||
this.sslConfig.entrys.forEach(item => {
|
||||
if (item && item.sourceId === row.id) {
|
||||
const index = this.sslConfig.entrys.findIndex((d) => d.sourceId === row.id);
|
||||
removeKeys.push(index);
|
||||
}
|
||||
});
|
||||
removeKeys.forEach(index => {
|
||||
if (index !== -1) {
|
||||
this.sslConfig.entrys.splice(index, 1);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
changeCheck(row) {
|
||||
if (row.default) {
|
||||
this.sslConfig.entrys.forEach(item => {
|
||||
if (item && item.sourceId !== row.id) {
|
||||
item.default = false;
|
||||
}
|
||||
});
|
||||
row.default = true;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .el-form-item {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.ms-el-form-item__content >>> .el-form-item__content {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
padding: 3px 5px;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #783887;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,207 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('test_track.case.import.import_file')"
|
||||
:visible.sync="dialogVisible"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
width="500px"
|
||||
:before-close="handleClose">
|
||||
<el-form :model="currentConfig" label-width="100px" v-loading="result.loading" ref="form">
|
||||
<el-row>
|
||||
<el-form-item :label="$t('commons.password')" prop="password">
|
||||
<el-input size="small" v-model="currentConfig.password" clearable show-password/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-upload
|
||||
class="jar-upload"
|
||||
drag
|
||||
action="#"
|
||||
:http-request="upload"
|
||||
:limit="1"
|
||||
:beforeUpload="uploadValidate"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:file-list="fileList"
|
||||
ref="fileUpload">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
|
||||
<div class="el-upload__tip" slot="tip">{{$t('api_test.api_import.file_size_limit')}},支持p12,jks,pfx格式</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-col>
|
||||
<div class="buttons">
|
||||
<el-button type="primary" size="small" @click="save('add')">{{$t('commons.save')}}</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "SSLFileUpload",
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
dialogVisible: false,
|
||||
result: {},
|
||||
currentConfig: {
|
||||
password: '',
|
||||
fileName: '',
|
||||
},
|
||||
rules: {
|
||||
name: [
|
||||
{required: true, message: this.$t('commons.input_name'), trigger: 'blur'},
|
||||
{max: 60, message: this.$t('commons.input_limit', [1, 60]), trigger: 'blur'}
|
||||
],
|
||||
description: [
|
||||
{max: 250, message: this.$t('commons.input_limit', [1, 250]), trigger: 'blur'}
|
||||
],
|
||||
},
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
props: {
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
sslConfig: {},
|
||||
callback: {
|
||||
type: Function
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
config() {
|
||||
this.currentConfig = {
|
||||
id: '',
|
||||
name: '',
|
||||
fileName: ''
|
||||
};
|
||||
if (this.config.fileName) {
|
||||
this.fileList = [{name: this.config.fileName}];
|
||||
} else {
|
||||
this.fileList = [];
|
||||
}
|
||||
Object.assign(this.currentConfig, this.config);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
Object.assign(this.currentConfig, this.config);
|
||||
},
|
||||
methods: {
|
||||
upload(file) {
|
||||
this.fileList.push(file.file)
|
||||
},
|
||||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
this.fileList = [];
|
||||
},
|
||||
uploadValidate(file, fileList) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (suffix !== 'jks' && suffix !== 'p12' && suffix !== 'pfx') {
|
||||
this.$warning(this.$t('api_test.api_import.suffixFormatErr'));
|
||||
return false;
|
||||
}
|
||||
if (file.size / 1024 / 1024 > 30) {
|
||||
this.$warning(this.$t('jar_config.upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
if (this.sslConfig.files) {
|
||||
let isFlag = false;
|
||||
this.sslConfig.files.forEach(item => {
|
||||
if (item && item.name === file.name) {
|
||||
isFlag = true;
|
||||
}
|
||||
})
|
||||
if (isFlag) {
|
||||
this.$warning("文件已经存在!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
save(type) {
|
||||
this.$refs['form'].validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.fileList <= 0) {
|
||||
this.$warning(this.$t('commons.please_upload'));
|
||||
return;
|
||||
}
|
||||
if (this.callback) {
|
||||
this.dialogVisible = false;
|
||||
this.callback(this.currentConfig, this.fileList[0]);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
clear() {
|
||||
this.currentConfig.password = "";
|
||||
this.currentConfig.id = "";
|
||||
this.fileList = [];
|
||||
},
|
||||
open(row) {
|
||||
this.clear();
|
||||
if (row) {
|
||||
this.currentConfig.password = row.password;
|
||||
this.currentConfig.id = row.id;
|
||||
this.fileList.push({name: row.name});
|
||||
}
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.el-divider {
|
||||
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.jar-upload {
|
||||
text-align: center;
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
.jar-upload >>> .el-upload {
|
||||
width: 100%;
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.jar-upload >>> .el-upload-dragger {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-form {
|
||||
border: solid #E1E1E1 1px;
|
||||
margin: 10px 0;
|
||||
padding: 30px 20px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
margin-top: 10px;
|
||||
margin-bottom: -10px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -20,6 +20,21 @@ export class Environment extends BaseConfig {
|
|||
}
|
||||
}
|
||||
|
||||
export class SSLConfig extends BaseConfig {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
this.entrys = [];
|
||||
this.files = [];
|
||||
this.set(options);
|
||||
this.sets({files: KeyValue}, options);
|
||||
this.sets({entrys: KeyValue}, options);
|
||||
}
|
||||
|
||||
initOptions(options = {}) {
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
export class Config extends BaseConfig {
|
||||
constructor(options = {}) {
|
||||
super();
|
||||
|
@ -27,7 +42,7 @@ export class Config extends BaseConfig {
|
|||
this.httpConfig = undefined;
|
||||
this.databaseConfigs = [];
|
||||
this.tcpConfig = undefined;
|
||||
|
||||
this.sslConfig = {};
|
||||
this.set(options);
|
||||
this.sets({databaseConfigs: DatabaseConfig}, options);
|
||||
}
|
||||
|
@ -35,6 +50,7 @@ export class Config extends BaseConfig {
|
|||
initOptions(options = {}) {
|
||||
this.commonConfig = new CommonConfig(options.commonConfig);
|
||||
this.httpConfig = new HttpConfig(options.httpConfig);
|
||||
this.sslConfig = new SSLConfig(options.sslConfig);
|
||||
options.databaseConfigs = options.databaseConfigs || [];
|
||||
options.tcpConfig = new TCPConfig(options.tcpConfig);
|
||||
return options;
|
||||
|
|
|
@ -159,6 +159,15 @@ export default {
|
|||
table: {
|
||||
select_tip: "Item {0} data is selected"
|
||||
},
|
||||
ssl: {
|
||||
config: "Config",
|
||||
files: "Files",
|
||||
entry: "Entry",
|
||||
original_as_name: "Original as name",
|
||||
new_as_name: "New name",
|
||||
source: "Source",
|
||||
default: "Default"
|
||||
},
|
||||
date: {
|
||||
select_date: 'Select date',
|
||||
start_date: 'Start date',
|
||||
|
|
|
@ -160,6 +160,15 @@ export default {
|
|||
table: {
|
||||
select_tip: "已选中 {0} 条数据"
|
||||
},
|
||||
ssl: {
|
||||
config: "证书配置",
|
||||
files: "证书文件",
|
||||
entry: "证书条目",
|
||||
original_as_name: "原有别名",
|
||||
new_as_name: "新别名",
|
||||
source: "来源",
|
||||
default: "是否默认"
|
||||
},
|
||||
date: {
|
||||
select_date: '选择日期',
|
||||
start_date: '开始日期',
|
||||
|
|
|
@ -160,6 +160,15 @@ export default {
|
|||
table: {
|
||||
select_tip: "已選中 {0} 條數據"
|
||||
},
|
||||
ssl: {
|
||||
config: "證書配置",
|
||||
files: "證書文件",
|
||||
entry: "證書條目",
|
||||
original_as_name: "原有別名",
|
||||
new_as_name: "新别名",
|
||||
source: "來源",
|
||||
default: "是否默認"
|
||||
},
|
||||
date: {
|
||||
select_date: '選擇日期',
|
||||
start_date: '開始日期',
|
||||
|
|
Loading…
Reference in New Issue