Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MeterSphere
metersphere
提交
51351feb
M
metersphere
项目概览
MeterSphere
/
metersphere
上一次同步 接近 3 年
通知
25
Star
1
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
metersphere
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
51351feb
编写于
5月 08, 2021
作者:
F
fit2-zhao
提交者:
BugKing
5月 13, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat(系统设置): 增加SSL证书认证
上级
76223055
变更
23
隐藏空白更改
内联
并排
Showing
23 changed file
with
1492 addition
and
14 deletion
+1492
-14
backend/src/main/java/io/metersphere/api/controller/ApiTestEnvironmentController.java
...ersphere/api/controller/ApiTestEnvironmentController.java
+18
-4
backend/src/main/java/io/metersphere/api/dto/ApiTestEnvironmentDTO.java
...in/java/io/metersphere/api/dto/ApiTestEnvironmentDTO.java
+11
-0
backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java
...tersphere/api/dto/definition/request/ParameterConfig.java
+6
-0
backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java
...pi/dto/definition/request/sampler/MsHTTPSamplerProxy.java
+70
-3
backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java
...phere/api/dto/scenario/environment/EnvironmentConfig.java
+3
-0
backend/src/main/java/io/metersphere/api/dto/ssl/KeyStoreConfig.java
.../main/java/io/metersphere/api/dto/ssl/KeyStoreConfig.java
+28
-0
backend/src/main/java/io/metersphere/api/dto/ssl/KeyStoreEntry.java
...c/main/java/io/metersphere/api/dto/ssl/KeyStoreEntry.java
+15
-0
backend/src/main/java/io/metersphere/api/dto/ssl/KeyStoreFile.java
...rc/main/java/io/metersphere/api/dto/ssl/KeyStoreFile.java
+15
-0
backend/src/main/java/io/metersphere/api/dto/ssl/MsKeyStore.java
.../src/main/java/io/metersphere/api/dto/ssl/MsKeyStore.java
+18
-0
backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java
...io/metersphere/api/service/ApiTestEnvironmentService.java
+16
-0
backend/src/main/java/io/metersphere/api/service/CommandService.java
.../main/java/io/metersphere/api/service/CommandService.java
+213
-0
backend/src/main/java/io/metersphere/commons/utils/FileUtils.java
...src/main/java/io/metersphere/commons/utils/FileUtils.java
+42
-5
backend/src/main/java/org/apache/jmeter/config/KeystoreConfig.java
...rc/main/java/org/apache/jmeter/config/KeystoreConfig.java
+174
-0
backend/src/main/java/org/apache/jmeter/util/SSLManager.java
backend/src/main/java/org/apache/jmeter/util/SSLManager.java
+378
-0
frontend/src/business/components/api/definition/components/request/http/ApiAdvancedConfig.vue
.../definition/components/request/http/ApiAdvancedConfig.vue
+8
-0
frontend/src/business/components/api/definition/model/ApiTestModel.js
.../business/components/api/definition/model/ApiTestModel.js
+1
-0
frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue
...nents/api/test/components/environment/EnvironmentEdit.vue
+27
-1
frontend/src/business/components/api/test/components/environment/EnvironmentSSLConfig.vue
.../api/test/components/environment/EnvironmentSSLConfig.vue
+198
-0
frontend/src/business/components/api/test/components/environment/SSLFileUpload.vue
...ponents/api/test/components/environment/SSLFileUpload.vue
+207
-0
frontend/src/business/components/api/test/model/EnvironmentModel.js
...rc/business/components/api/test/model/EnvironmentModel.js
+17
-1
frontend/src/i18n/en-US.js
frontend/src/i18n/en-US.js
+9
-0
frontend/src/i18n/zh-CN.js
frontend/src/i18n/zh-CN.js
+9
-0
frontend/src/i18n/zh-TW.js
frontend/src/i18n/zh-TW.js
+9
-0
未找到文件。
backend/src/main/java/io/metersphere/api/controller/ApiTestEnvironmentController.java
浏览文件 @
51351feb
...
...
@@ -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
apiTestEnvironmentWithBLOB
s
)
{
return
apiTestEnvironmentService
.
add
(
apiTestEnvironmentWithBLOBs
);
public
String
create
(
@RequestPart
(
"request"
)
ApiTestEnvironmentDTO
apiTestEnvironmentWithBLOBs
,
@RequestPart
(
value
=
"files"
)
List
<
MultipartFile
>
sslFile
s
)
{
return
apiTestEnvironmentService
.
add
(
apiTestEnvironmentWithBLOBs
,
sslFiles
);
}
@PostMapping
(
value
=
"/update"
)
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_MANAGER
,
RoleConstants
.
TEST_USER
,},
logical
=
Logical
.
OR
)
public
void
update
(
@Request
Body
ApiTestEnvironmentWithBLOBs
apiTestEnvironment
)
{
apiTestEnvironmentService
.
update
(
apiTestEnvironment
);
public
void
update
(
@Request
Part
(
"request"
)
ApiTestEnvironmentDTO
apiTestEnvironment
,
@RequestPart
(
value
=
"files"
)
List
<
MultipartFile
>
sslFiles
)
{
apiTestEnvironmentService
.
update
(
apiTestEnvironment
,
sslFiles
);
}
@GetMapping
(
"/delete/{id}"
)
...
...
backend/src/main/java/io/metersphere/api/dto/ApiTestEnvironmentDTO.java
0 → 100644
浏览文件 @
51351feb
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
;
}
backend/src/main/java/io/metersphere/api/dto/definition/request/ParameterConfig.java
浏览文件 @
51351feb
...
...
@@ -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
<>();
/**
* 公共场景参数
*/
...
...
backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java
浏览文件 @
51351feb
...
...
@@ -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
;
}
...
...
backend/src/main/java/io/metersphere/api/dto/scenario/environment/EnvironmentConfig.java
浏览文件 @
51351feb
...
...
@@ -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
();
}
}
backend/src/main/java/io/metersphere/api/dto/ssl/KeyStoreConfig.java
0 → 100644
浏览文件 @
51351feb
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
;
}
}
backend/src/main/java/io/metersphere/api/dto/ssl/KeyStoreEntry.java
0 → 100644
浏览文件 @
51351feb
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
;
}
backend/src/main/java/io/metersphere/api/dto/ssl/KeyStoreFile.java
0 → 100644
浏览文件 @
51351feb
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
;
}
backend/src/main/java/io/metersphere/api/dto/ssl/MsKeyStore.java
0 → 100644
浏览文件 @
51351feb
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
;
}
}
backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java
浏览文件 @
51351feb
...
...
@@ -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
();
...
...
backend/src/main/java/io/metersphere/api/service/CommandService.java
0 → 100644
浏览文件 @
51351feb
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
;
}
}
}
backend/src/main/java/io/metersphere/commons/utils/FileUtils.java
浏览文件 @
51351feb
...
...
@@ -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
)
{
...
...
backend/src/main/java/org/apache/jmeter/config/KeystoreConfig.java
0 → 100644
浏览文件 @
51351feb
/*
* 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
;
}
}
backend/src/main/java/org/apache/jmeter/util/SSLManager.java
0 → 100644
浏览文件 @
51351feb
/*
* 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
;
}
}
frontend/src/business/components/api/definition/components/request/http/ApiAdvancedConfig.vue
浏览文件 @
51351feb
...
...
@@ -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>
...
...
frontend/src/business/components/api/definition/model/ApiTestModel.js
浏览文件 @
51351feb
...
...
@@ -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
);
}
...
...
frontend/src/business/components/api/test/components/environment/EnvironmentEdit.vue
浏览文件 @
51351feb
...
...
@@ -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
;
}
...
...
frontend/src/business/components/api/test/components/environment/EnvironmentSSLConfig.vue
0 → 100644
浏览文件 @
51351feb
<
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
>
frontend/src/business/components/api/test/components/environment/SSLFileUpload.vue
0 → 100644
浏览文件 @
51351feb
<
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
>
frontend/src/business/components/api/test/model/EnvironmentModel.js
浏览文件 @
51351feb
...
...
@@ -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
;
...
...
frontend/src/i18n/en-US.js
浏览文件 @
51351feb
...
...
@@ -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
'
,
...
...
frontend/src/i18n/zh-CN.js
浏览文件 @
51351feb
...
...
@@ -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
:
'
开始日期
'
,
...
...
frontend/src/i18n/zh-TW.js
浏览文件 @
51351feb
...
...
@@ -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
:
'
開始日期
'
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录