feat(系统设置): 增加SSL证书认证
This commit is contained in:
parent
7345c0546a
commit
4e221441c8
|
@ -2,7 +2,10 @@ package io.metersphere.api.controller;
|
||||||
|
|
||||||
import com.github.pagehelper.Page;
|
import com.github.pagehelper.Page;
|
||||||
import com.github.pagehelper.PageHelper;
|
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.ApiTestEnvironmentService;
|
||||||
|
import io.metersphere.api.service.CommandService;
|
||||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||||
import io.metersphere.commons.constants.RoleConstants;
|
import io.metersphere.commons.constants.RoleConstants;
|
||||||
import io.metersphere.commons.utils.PageUtils;
|
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.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -25,6 +29,8 @@ public class ApiTestEnvironmentController {
|
||||||
ApiTestEnvironmentService apiTestEnvironmentService;
|
ApiTestEnvironmentService apiTestEnvironmentService;
|
||||||
@Resource
|
@Resource
|
||||||
private CheckPermissionService checkPermissionService;
|
private CheckPermissionService checkPermissionService;
|
||||||
|
@Resource
|
||||||
|
private CommandService commandService;
|
||||||
|
|
||||||
@GetMapping("/list/{projectId}")
|
@GetMapping("/list/{projectId}")
|
||||||
public List<ApiTestEnvironmentWithBLOBs> list(@PathVariable String projectId) {
|
public List<ApiTestEnvironmentWithBLOBs> list(@PathVariable String projectId) {
|
||||||
|
@ -34,6 +40,7 @@ public class ApiTestEnvironmentController {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询指定项目和指定名称的环境
|
* 查询指定项目和指定名称的环境
|
||||||
|
*
|
||||||
* @param goPage
|
* @param goPage
|
||||||
* @param pageSize
|
* @param pageSize
|
||||||
* @param environmentRequest
|
* @param environmentRequest
|
||||||
|
@ -54,16 +61,23 @@ public class ApiTestEnvironmentController {
|
||||||
return apiTestEnvironmentService.get(id);
|
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")
|
@PostMapping("/add")
|
||||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
||||||
public String add(@RequestBody ApiTestEnvironmentWithBLOBs apiTestEnvironmentWithBLOBs) {
|
public String create(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironmentWithBLOBs, @RequestPart(value = "files") List<MultipartFile> sslFiles) {
|
||||||
return apiTestEnvironmentService.add(apiTestEnvironmentWithBLOBs);
|
return apiTestEnvironmentService.add(apiTestEnvironmentWithBLOBs, sslFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/update")
|
@PostMapping(value = "/update")
|
||||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER,}, logical = Logical.OR)
|
||||||
public void update(@RequestBody ApiTestEnvironmentWithBLOBs apiTestEnvironment) {
|
public void update(@RequestPart("request") ApiTestEnvironmentDTO apiTestEnvironment, @RequestPart(value = "files") List<MultipartFile> sslFiles) {
|
||||||
apiTestEnvironmentService.update(apiTestEnvironment);
|
apiTestEnvironmentService.update(apiTestEnvironment, sslFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/delete/{id}")
|
@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.definition.request.variable.ScenarioVariable;
|
||||||
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
|
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
|
||||||
|
import io.metersphere.api.dto.ssl.MsKeyStore;
|
||||||
import io.metersphere.commons.utils.ScriptEngineUtils;
|
import io.metersphere.commons.utils.ScriptEngineUtils;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.apache.jmeter.config.Arguments;
|
import org.apache.jmeter.config.Arguments;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -15,6 +17,10 @@ public class ParameterConfig {
|
||||||
* 环境配置
|
* 环境配置
|
||||||
*/
|
*/
|
||||||
private Map<String, EnvironmentConfig> config;
|
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.HttpConfig;
|
||||||
import io.metersphere.api.dto.scenario.HttpConfigCondition;
|
import io.metersphere.api.dto.scenario.HttpConfigCondition;
|
||||||
import io.metersphere.api.dto.scenario.KeyValue;
|
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.ApiDefinitionService;
|
||||||
import io.metersphere.api.service.ApiTestCaseService;
|
import io.metersphere.api.service.ApiTestCaseService;
|
||||||
|
import io.metersphere.api.service.CommandService;
|
||||||
import io.metersphere.base.domain.ApiDefinition;
|
import io.metersphere.base.domain.ApiDefinition;
|
||||||
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
import io.metersphere.base.domain.ApiDefinitionWithBLOBs;
|
||||||
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
|
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.constants.RunModeConstants;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
|
import io.metersphere.commons.utils.FileUtils;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.commons.utils.ScriptEngineUtils;
|
import io.metersphere.commons.utils.ScriptEngineUtils;
|
||||||
import io.metersphere.track.service.TestPlanApiCaseService;
|
import io.metersphere.track.service.TestPlanApiCaseService;
|
||||||
|
@ -34,6 +38,7 @@ import lombok.EqualsAndHashCode;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.jmeter.config.Arguments;
|
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.Header;
|
||||||
import org.apache.jmeter.protocol.http.control.HeaderManager;
|
import org.apache.jmeter.protocol.http.control.HeaderManager;
|
||||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||||
|
@ -110,6 +115,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
||||||
@JSONField(ordinal = 37)
|
@JSONField(ordinal = 37)
|
||||||
private Boolean isRefEnvironment;
|
private Boolean isRefEnvironment;
|
||||||
|
|
||||||
|
@JSONField(ordinal = 38)
|
||||||
|
private String alias;
|
||||||
|
|
||||||
private void setRefElement() {
|
private void setRefElement() {
|
||||||
try {
|
try {
|
||||||
ApiDefinitionService apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class);
|
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.addEnvironmentVariables(httpSamplerTree, this.getName(), config.getConfig().get(this.getProjectId()));
|
||||||
MsDNSCacheManager.addEnvironmentDNS(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)) {
|
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||||
for (MsTestElement el : hashTree) {
|
for (MsTestElement el : hashTree) {
|
||||||
el.toHashTree(httpSamplerTree, el.getHashTree(), config);
|
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;
|
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() {
|
private boolean isRest() {
|
||||||
return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0;
|
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.DatabaseConfig;
|
||||||
import io.metersphere.api.dto.scenario.HttpConfig;
|
import io.metersphere.api.dto.scenario.HttpConfig;
|
||||||
import io.metersphere.api.dto.scenario.TCPConfig;
|
import io.metersphere.api.dto.scenario.TCPConfig;
|
||||||
|
import io.metersphere.api.dto.ssl.KeyStoreConfig;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -14,11 +15,13 @@ public class EnvironmentConfig {
|
||||||
private HttpConfig httpConfig;
|
private HttpConfig httpConfig;
|
||||||
private List<DatabaseConfig> databaseConfigs;
|
private List<DatabaseConfig> databaseConfigs;
|
||||||
private TCPConfig tcpConfig;
|
private TCPConfig tcpConfig;
|
||||||
|
private KeyStoreConfig sslConfig;
|
||||||
|
|
||||||
public EnvironmentConfig() {
|
public EnvironmentConfig() {
|
||||||
this.commonConfig = new CommonConfig();
|
this.commonConfig = new CommonConfig();
|
||||||
this.httpConfig = new HttpConfig();
|
this.httpConfig = new HttpConfig();
|
||||||
this.databaseConfigs = new ArrayList<>();
|
this.databaseConfigs = new ArrayList<>();
|
||||||
this.tcpConfig = new TCPConfig();
|
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.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.metersphere.api.dto.ApiTestEnvironmentDTO;
|
||||||
import io.metersphere.api.dto.mockconfig.MockConfigStaticData;
|
import io.metersphere.api.dto.mockconfig.MockConfigStaticData;
|
||||||
import io.metersphere.base.domain.ApiTestEnvironmentExample;
|
import io.metersphere.base.domain.ApiTestEnvironmentExample;
|
||||||
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
|
||||||
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
import io.metersphere.base.mapper.ApiTestEnvironmentMapper;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
|
import io.metersphere.commons.utils.FileUtils;
|
||||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||||
import io.metersphere.controller.request.EnvironmentRequest;
|
import io.metersphere.controller.request.EnvironmentRequest;
|
||||||
import io.metersphere.dto.BaseSystemConfigDTO;
|
import io.metersphere.dto.BaseSystemConfigDTO;
|
||||||
|
@ -16,6 +18,7 @@ import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -72,6 +75,19 @@ public class ApiTestEnvironmentService {
|
||||||
return apiTestEnvironmentWithBLOBs.getId();
|
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) {
|
private void checkEnvironmentExist(ApiTestEnvironmentWithBLOBs environment) {
|
||||||
if (environment.getName() != null) {
|
if (environment.getName() != null) {
|
||||||
ApiTestEnvironmentExample example = new ApiTestEnvironmentExample();
|
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.commons.exception.MSException;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.aspectj.util.FileUtil;
|
import org.aspectj.util.FileUtil;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
public class FileUtils {
|
public class FileUtils {
|
||||||
public static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
|
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)) {
|
if (CollectionUtils.isNotEmpty(bodyUploadIds) && CollectionUtils.isNotEmpty(bodyFiles)) {
|
||||||
File testDir = new File(BODY_FILE_DIR);
|
File testDir = new File(filePath);
|
||||||
if (!testDir.exists()) {
|
if (!testDir.exists()) {
|
||||||
testDir.mkdirs();
|
testDir.mkdirs();
|
||||||
}
|
}
|
||||||
for (int i = 0; i < bodyUploadIds.size(); i++) {
|
for (int i = 0; i < bodyUploadIds.size(); i++) {
|
||||||
MultipartFile item = bodyFiles.get(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)) {
|
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
|
||||||
file.createNewFile();
|
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) {
|
} catch (IOException e) {
|
||||||
LogUtil.error(e);
|
LogUtil.error(e);
|
||||||
MSException.throwException(Translator.get("upload_fail"));
|
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) {
|
public static String uploadFile(MultipartFile uploadFile, String path, String name) {
|
||||||
if (uploadFile == null) {
|
if (uploadFile == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -53,7 +90,7 @@ public class FileUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String uploadFile(MultipartFile uploadFile, String path) {
|
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) {
|
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"/>
|
<el-input-number size="small" :disabled="isReadOnly" v-model="request.responseTimeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||||
</span>
|
</span>
|
||||||
</el-row>
|
</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">
|
<el-row style="margin: 20px">
|
||||||
<span style="margin-right: 10px">
|
<span style="margin-right: 10px">
|
||||||
<el-checkbox class="follow-redirects-item" v-model="request.followRedirects">{{$t('api_test.request.follow_redirects')}}</el-checkbox>
|
<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.files = undefined;
|
||||||
this.enable = undefined;
|
this.enable = undefined;
|
||||||
this.uuid = undefined;
|
this.uuid = undefined;
|
||||||
|
this.time = undefined;
|
||||||
this.contentType = undefined;
|
this.contentType = undefined;
|
||||||
this.set(options);
|
this.set(options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
<el-tab-pane :label="$t('api_test.environment.tcp_config')" name="tcp">
|
<el-tab-pane :label="$t('api_test.environment.tcp_config')" name="tcp">
|
||||||
<ms-tcp-config :config="environment.config.tcpConfig"/>
|
<ms-tcp-config :config="environment.config.tcpConfig"/>
|
||||||
</el-tab-pane>
|
</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>
|
</el-tabs>
|
||||||
|
|
||||||
<div class="environment-footer">
|
<div class="environment-footer">
|
||||||
|
@ -44,7 +47,10 @@
|
||||||
import MsDatabaseConfig from "../request/database/DatabaseConfig";
|
import MsDatabaseConfig from "../request/database/DatabaseConfig";
|
||||||
import MsEnvironmentHttpConfig from "./EnvironmentHttpConfig";
|
import MsEnvironmentHttpConfig from "./EnvironmentHttpConfig";
|
||||||
import MsEnvironmentCommonConfig from "./EnvironmentCommonConfig";
|
import MsEnvironmentCommonConfig from "./EnvironmentCommonConfig";
|
||||||
|
import MsEnvironmentSSLConfig from "./EnvironmentSSLConfig";
|
||||||
|
|
||||||
import MsTcpConfig from "@/business/components/api/test/components/request/tcp/TcpConfig";
|
import MsTcpConfig from "@/business/components/api/test/components/request/tcp/TcpConfig";
|
||||||
|
import {getUUID} from "@/common/js/utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "EnvironmentEdit",
|
name: "EnvironmentEdit",
|
||||||
|
@ -52,6 +58,7 @@
|
||||||
MsTcpConfig,
|
MsTcpConfig,
|
||||||
MsEnvironmentCommonConfig,
|
MsEnvironmentCommonConfig,
|
||||||
MsEnvironmentHttpConfig,
|
MsEnvironmentHttpConfig,
|
||||||
|
MsEnvironmentSSLConfig,
|
||||||
MsDatabaseConfig, MsApiHostTable, MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables
|
MsDatabaseConfig, MsApiHostTable, MsDialogFooter, MsApiKeyValue, MsApiScenarioVariables
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -97,13 +104,32 @@
|
||||||
});
|
});
|
||||||
return isValidate;
|
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) {
|
_save(environment) {
|
||||||
|
let bodyFiles = this.geFiles(environment);
|
||||||
let param = this.buildParam(environment);
|
let param = this.buildParam(environment);
|
||||||
let url = '/api/environment/add';
|
let url = '/api/environment/add';
|
||||||
if (param.id) {
|
if (param.id) {
|
||||||
url = '/api/environment/update';
|
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) {
|
if (!param.id) {
|
||||||
environment.id = response.data;
|
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 {
|
export class Config extends BaseConfig {
|
||||||
constructor(options = {}) {
|
constructor(options = {}) {
|
||||||
super();
|
super();
|
||||||
|
@ -27,7 +42,7 @@ export class Config extends BaseConfig {
|
||||||
this.httpConfig = undefined;
|
this.httpConfig = undefined;
|
||||||
this.databaseConfigs = [];
|
this.databaseConfigs = [];
|
||||||
this.tcpConfig = undefined;
|
this.tcpConfig = undefined;
|
||||||
|
this.sslConfig = {};
|
||||||
this.set(options);
|
this.set(options);
|
||||||
this.sets({databaseConfigs: DatabaseConfig}, options);
|
this.sets({databaseConfigs: DatabaseConfig}, options);
|
||||||
}
|
}
|
||||||
|
@ -35,6 +50,7 @@ export class Config extends BaseConfig {
|
||||||
initOptions(options = {}) {
|
initOptions(options = {}) {
|
||||||
this.commonConfig = new CommonConfig(options.commonConfig);
|
this.commonConfig = new CommonConfig(options.commonConfig);
|
||||||
this.httpConfig = new HttpConfig(options.httpConfig);
|
this.httpConfig = new HttpConfig(options.httpConfig);
|
||||||
|
this.sslConfig = new SSLConfig(options.sslConfig);
|
||||||
options.databaseConfigs = options.databaseConfigs || [];
|
options.databaseConfigs = options.databaseConfigs || [];
|
||||||
options.tcpConfig = new TCPConfig(options.tcpConfig);
|
options.tcpConfig = new TCPConfig(options.tcpConfig);
|
||||||
return options;
|
return options;
|
||||||
|
|
|
@ -159,6 +159,15 @@ export default {
|
||||||
table: {
|
table: {
|
||||||
select_tip: "Item {0} data is selected"
|
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: {
|
date: {
|
||||||
select_date: 'Select date',
|
select_date: 'Select date',
|
||||||
start_date: 'Start date',
|
start_date: 'Start date',
|
||||||
|
|
|
@ -160,6 +160,15 @@ export default {
|
||||||
table: {
|
table: {
|
||||||
select_tip: "已选中 {0} 条数据"
|
select_tip: "已选中 {0} 条数据"
|
||||||
},
|
},
|
||||||
|
ssl: {
|
||||||
|
config: "证书配置",
|
||||||
|
files: "证书文件",
|
||||||
|
entry: "证书条目",
|
||||||
|
original_as_name: "原有别名",
|
||||||
|
new_as_name: "新别名",
|
||||||
|
source: "来源",
|
||||||
|
default: "是否默认"
|
||||||
|
},
|
||||||
date: {
|
date: {
|
||||||
select_date: '选择日期',
|
select_date: '选择日期',
|
||||||
start_date: '开始日期',
|
start_date: '开始日期',
|
||||||
|
|
|
@ -160,6 +160,15 @@ export default {
|
||||||
table: {
|
table: {
|
||||||
select_tip: "已選中 {0} 條數據"
|
select_tip: "已選中 {0} 條數據"
|
||||||
},
|
},
|
||||||
|
ssl: {
|
||||||
|
config: "證書配置",
|
||||||
|
files: "證書文件",
|
||||||
|
entry: "證書條目",
|
||||||
|
original_as_name: "原有別名",
|
||||||
|
new_as_name: "新别名",
|
||||||
|
source: "來源",
|
||||||
|
default: "是否默認"
|
||||||
|
},
|
||||||
date: {
|
date: {
|
||||||
select_date: '選擇日期',
|
select_date: '選擇日期',
|
||||||
start_date: '開始日期',
|
start_date: '開始日期',
|
||||||
|
|
Loading…
Reference in New Issue