fix(测试跟踪): 缺陷平台请求转发添加白名单
This commit is contained in:
parent
0b14520dac
commit
567fed9375
|
@ -25,9 +25,11 @@ public class ResourceController {
|
||||||
return resourceService.getMdImage(fileName);
|
return resourceService.getMdImage(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/md/get/url")
|
@GetMapping(value = "/md/get/path")
|
||||||
public ResponseEntity<byte[]> getFileByUrl(@RequestParam ("url") String url, @RequestParam (value = "platform", required = false) String platform) {
|
public ResponseEntity<byte[]> getFileByPath(@RequestParam ("path") String path,
|
||||||
return resourceService.getMdImageByUrl(url, platform);
|
@RequestParam (value = "platform") String platform,
|
||||||
|
@RequestParam (value = "workspaceId") String workspaceId) {
|
||||||
|
return resourceService.getMdImageByPath(path, platform, workspaceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/ui/get")
|
@GetMapping(value = "/ui/get")
|
||||||
|
|
|
@ -3,15 +3,12 @@ package io.metersphere.service;
|
||||||
import io.metersphere.commons.exception.MSException;
|
import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.FileUtils;
|
import io.metersphere.commons.utils.FileUtils;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.commons.utils.SessionUtils;
|
|
||||||
import io.metersphere.controller.request.MdUploadRequest;
|
import io.metersphere.controller.request.MdUploadRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.track.issue.IssueFactory;
|
import io.metersphere.track.issue.IssueFactory;
|
||||||
import io.metersphere.track.request.testcase.IssuesRequest;
|
import io.metersphere.track.request.testcase.IssuesRequest;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.core.io.FileSystemResource;
|
import org.springframework.core.io.FileSystemResource;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -99,21 +96,14 @@ public class ResourceService {
|
||||||
* http 代理
|
* http 代理
|
||||||
* 如果当前访问地址是 https,直接访问 http 的图片资源
|
* 如果当前访问地址是 https,直接访问 http 的图片资源
|
||||||
* 由于浏览器的安全机制,http 会被转成 https
|
* 由于浏览器的安全机制,http 会被转成 https
|
||||||
* @param url
|
* @param path
|
||||||
* @param platform
|
* @param platform
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ResponseEntity<byte[]> getMdImageByUrl(String url, String platform) {
|
public ResponseEntity<byte[]> getMdImageByPath(String path, String platform, String workspaceId) {
|
||||||
if (url.contains("md/get/url")) {
|
IssuesRequest issuesRequest = new IssuesRequest();
|
||||||
MSException.throwException(Translator.get("invalid_parameter"));
|
issuesRequest.setWorkspaceId(workspaceId);
|
||||||
}
|
return IssueFactory.createPlatform(platform, issuesRequest)
|
||||||
if (StringUtils.isNotBlank(platform)) {
|
.proxyForGet(path, byte[].class);
|
||||||
IssuesRequest issuesRequest = new IssuesRequest();
|
|
||||||
issuesRequest.setProjectId(SessionUtils.getCurrentProjectId());
|
|
||||||
issuesRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
|
||||||
return IssueFactory.createPlatform(platform, issuesRequest)
|
|
||||||
.proxyForGet(url, byte[].class);
|
|
||||||
}
|
|
||||||
return restTemplate.exchange(url, HttpMethod.GET, null, byte[].class);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
|
@ -70,6 +72,8 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
||||||
protected AttachmentService attachmentService;
|
protected AttachmentService attachmentService;
|
||||||
protected AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
protected AttachmentModuleRelationMapper attachmentModuleRelationMapper;
|
||||||
|
|
||||||
|
public static final String PROXY_PATH = "/resource/md/get/path?platform=%s&workspaceId=%s&path=%s";
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
@ -101,6 +105,10 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
||||||
this.attachmentModuleRelationMapper = CommonBeanFactory.getBean(AttachmentModuleRelationMapper.class);
|
this.attachmentModuleRelationMapper = CommonBeanFactory.getBean(AttachmentModuleRelationMapper.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getProxyPath(String path) {
|
||||||
|
return String.format(PROXY_PATH, this.key, this.workspaceId, URLEncoder.encode(path, StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
protected String getPlatformConfig(String platform) {
|
protected String getPlatformConfig(String platform) {
|
||||||
IntegrationRequest request = new IntegrationRequest();
|
IntegrationRequest request = new IntegrationRequest();
|
||||||
if (StringUtils.isBlank(workspaceId)) {
|
if (StringUtils.isBlank(workspaceId)) {
|
||||||
|
@ -355,7 +363,7 @@ public abstract class AbstractIssuePlatform implements IssuesPlatform {
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
try {
|
try {
|
||||||
String path = matcher.group(2);
|
String path = matcher.group(2);
|
||||||
if (!path.contains("/resource/md/get/url")) {
|
if (!path.contains("/resource/md/get/url") && !path.contains("/resource/md/get/path")) {
|
||||||
if (path.contains("/resource/md/get/")) { // 兼容旧数据
|
if (path.contains("/resource/md/get/")) { // 兼容旧数据
|
||||||
String name = path.substring(path.indexOf("/resource/md/get/") + 17);
|
String name = path.substring(path.indexOf("/resource/md/get/") + 17);
|
||||||
files.add(new File(FileUtils.MD_IMAGE_DIR + "/" + name));
|
files.add(new File(FileUtils.MD_IMAGE_DIR + "/" + name));
|
||||||
|
|
|
@ -111,10 +111,10 @@ public interface IssuesPlatform {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get请求的代理
|
* Get请求的代理
|
||||||
* @param url
|
* @param path
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
ResponseEntity proxyForGet(String url, Class responseEntityClazz);
|
ResponseEntity proxyForGet(String path, Class responseEntityClazz);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步MS缺陷附件到第三方平台
|
* 同步MS缺陷附件到第三方平台
|
||||||
|
|
|
@ -30,9 +30,9 @@ import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.client.HttpClientErrorException;
|
import org.springframework.web.client.HttpClientErrorException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.ZoneId;
|
import java.time.ZoneId;
|
||||||
|
@ -126,10 +126,13 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
||||||
// 解析标签内容
|
// 解析标签内容
|
||||||
String name = getHyperLinkPathForImg("\\!\\[(.*?)\\]", StringEscapeUtils.unescapeJava(splitStr));
|
String name = getHyperLinkPathForImg("\\!\\[(.*?)\\]", StringEscapeUtils.unescapeJava(splitStr));
|
||||||
String path = getHyperLinkPathForImg("\\|(.*?)\\)", splitStr);
|
String path = getHyperLinkPathForImg("\\|(.*?)\\)", splitStr);
|
||||||
path = "/resource/md/get/url?platform=Jira&url=" + URLEncoder.encode(path, StandardCharsets.UTF_8);
|
try {
|
||||||
|
path = getProxyPath(new URI(path).getPath());
|
||||||
// 解析标签内容为图片超链接格式,进行替换
|
// 解析标签内容为图片超链接格式,进行替换
|
||||||
description = description.replace(splitStr, "\n\n![" + name + "](" + path + ")");
|
description = description.replace(splitStr, "\n\n![" + name + "](" + path + ")");
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
LogUtil.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
description = description.replace(splitStr, StringEscapeUtils.unescapeJava(splitStr.replace("MS附件:", "")));
|
description = description.replace(splitStr, StringEscapeUtils.unescapeJava(splitStr.replace("MS附件:", "")));
|
||||||
}
|
}
|
||||||
|
@ -154,14 +157,17 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
||||||
JSONObject attachment = attachments.getJSONObject(i);
|
JSONObject attachment = attachments.getJSONObject(i);
|
||||||
String filename = attachment.getString("filename");
|
String filename = attachment.getString("filename");
|
||||||
String content = attachment.getString("content");
|
String content = attachment.getString("content");
|
||||||
content = "/resource/md/get/url?platform=Jira&url=" + URLEncoder.encode(content, StandardCharsets.UTF_8);
|
try {
|
||||||
|
content = getProxyPath(new URI(content).getPath());
|
||||||
if (StringUtils.contains(attachment.getString("mimeType"), "image")) {
|
if (StringUtils.contains(attachment.get("mimeType").toString(), "image")) {
|
||||||
String contentUrl = "![" + filename + "](" + content + ")";
|
String contentUrl = "![" + filename + "](" + content + ")";
|
||||||
fileContentMap.put(filename, contentUrl);
|
fileContentMap.put(filename, contentUrl);
|
||||||
} else {
|
} else {
|
||||||
String contentUrl = "附件[" + filename + "]下载地址:" + content;
|
String contentUrl = "附件[" + filename + "]下载地址:" + content;
|
||||||
fileContentMap.put(filename, contentUrl);
|
fileContentMap.put(filename, contentUrl);
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -851,8 +857,8 @@ public class JiraPlatform extends AbstractIssuePlatform {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseEntity proxyForGet(String url, Class responseEntityClazz) {
|
public ResponseEntity proxyForGet(String path, Class responseEntityClazz) {
|
||||||
return jiraClientV2.proxyForGet(url, responseEntityClazz);
|
return jiraClientV2.proxyForGet(path, responseEntityClazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -453,7 +453,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
// get file name
|
// get file name
|
||||||
String originSubUrl = matcher.group(1);
|
String originSubUrl = matcher.group(1);
|
||||||
if (originSubUrl.contains("/url?url=")) {
|
if (originSubUrl.contains("/url?url=") || originSubUrl.contains("/path?")) {
|
||||||
String path = URLDecoder.decode(originSubUrl, StandardCharsets.UTF_8);
|
String path = URLDecoder.decode(originSubUrl, StandardCharsets.UTF_8);
|
||||||
String fileName;
|
String fileName;
|
||||||
if (path.indexOf("fileID") > 0) {
|
if (path.indexOf("fileID") > 0) {
|
||||||
|
@ -525,11 +525,18 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
name = name.replaceAll("&", "&");
|
name = name.replaceAll("&", "&");
|
||||||
path = zentaoClient.getBaseUrl() + path.replaceAll("&", "&");
|
path = path.replaceAll("&", "&");
|
||||||
}
|
}
|
||||||
// 专业版格式有差异,解析完会出现两个 /pro,去掉一个
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
path.replace("/pro/pro", "/pro");
|
for (String item : path.split("&")) {
|
||||||
path = "/resource/md/get/url?url=" + URLEncoder.encode(path, StandardCharsets.UTF_8);
|
// 去掉多余的参数
|
||||||
|
if (!StringUtils.containsAny(item, "platform", "workspaceId")) {
|
||||||
|
stringBuilder.append(item);
|
||||||
|
stringBuilder.append("&");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path = getProxyPath(stringBuilder.toString());
|
||||||
|
|
||||||
// 图片与描述信息之间需换行,否则无法预览图片
|
// 图片与描述信息之间需换行,否则无法预览图片
|
||||||
result = "\n\n![" + name + "](" + path + ")";
|
result = "\n\n![" + name + "](" + path + ")";
|
||||||
}
|
}
|
||||||
|
@ -637,4 +644,9 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
|
||||||
}
|
}
|
||||||
return platformStatusDTOS;
|
return platformStatusDTOS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity proxyForGet(String path, Class responseEntityClazz) {
|
||||||
|
return zentaoClient.proxyForGet(path, responseEntityClazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import io.metersphere.commons.exception.MSException;
|
||||||
import io.metersphere.commons.utils.EncryptUtils;
|
import io.metersphere.commons.utils.EncryptUtils;
|
||||||
import io.metersphere.commons.utils.EnvProxySelector;
|
import io.metersphere.commons.utils.EnvProxySelector;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||||
import org.apache.http.conn.ssl.TrustStrategy;
|
import org.apache.http.conn.ssl.TrustStrategy;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
@ -17,6 +18,8 @@ import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
import javax.net.ssl.SSLContext;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@ -70,4 +73,16 @@ public abstract class BaseClient {
|
||||||
protected Object getResultForObject(Class clazz,ResponseEntity<String> response) {
|
protected Object getResultForObject(Class clazz,ResponseEntity<String> response) {
|
||||||
return JSONObject.parseObject(getResult(response), clazz);
|
return JSONObject.parseObject(getResult(response), clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void validateProxyUrl(String url, String ...path) {
|
||||||
|
try {
|
||||||
|
if (!StringUtils.containsAny(new URI(url).getPath(), path)) {
|
||||||
|
// 只允许访问图片
|
||||||
|
MSException.throwException("illegal path");
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
LogUtil.error(e);
|
||||||
|
MSException.throwException("illegal path");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -294,8 +294,10 @@ public abstract class JiraAbstractClient extends BaseClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResponseEntity proxyForGet(String url, Class responseEntityClazz) {
|
public ResponseEntity proxyForGet(String path, Class responseEntityClazz) {
|
||||||
LogUtil.info("jira proxyForGet: " + url);
|
LogUtil.info("jira proxyForGet: " + path);
|
||||||
|
String url = this.ENDPOINT + path;
|
||||||
|
validateProxyUrl(url, "/secure/attachment", "/attachment/content");
|
||||||
return restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), responseEntityClazz);
|
return restTemplate.exchange(url, HttpMethod.GET, getAuthHttpEntity(), responseEntityClazz);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public abstract class ZentaoClient extends BaseClient {
|
public abstract class ZentaoClient extends BaseClient {
|
||||||
|
@ -248,4 +250,19 @@ public abstract class ZentaoClient extends BaseClient {
|
||||||
null, byte[].class, fileId, sessionId);
|
null, byte[].class, fileId, sessionId);
|
||||||
return response.getBody();
|
return response.getBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ResponseEntity proxyForGet(String path, Class responseEntityClazz) {
|
||||||
|
LogUtil.info("zentao proxyForGet: " + path);
|
||||||
|
String url = this.ENDPOINT + path;
|
||||||
|
try {
|
||||||
|
if (!StringUtils.containsAny(new URI(url).getPath(), "/index.php", "/file-read-")) {
|
||||||
|
// 只允许访问图片
|
||||||
|
MSException.throwException("illegal path");
|
||||||
|
}
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
LogUtil.error(e);
|
||||||
|
MSException.throwException("illegal path");
|
||||||
|
}
|
||||||
|
return restTemplate.exchange(url, HttpMethod.GET, null, responseEntityClazz);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class ZentaoGetClient extends ZentaoClient {
|
||||||
private static final String STORY_GET="&module=story&methodName=getProductStories¶ms=productID={key}&t=json&zentaosid=";
|
private static final String STORY_GET="&module=story&methodName=getProductStories¶ms=productID={key}&t=json&zentaosid=";
|
||||||
private static final String USER_GET="&module=user&methodName=getList&t=json&zentaosid=";
|
private static final String USER_GET="&module=user&methodName=getList&t=json&zentaosid=";
|
||||||
private static final String BUILDS_GET="&module=build&methodName=getProductBuildPairs&productID={0}&zentaosid={1}";
|
private static final String BUILDS_GET="&module=build&methodName=getProductBuildPairs&productID={0}&zentaosid={1}";
|
||||||
private static final String FILE_UPLOAD="&module=file&methodName=saveUpload¶ms=objectType={1},objectID={2},zentaosid={3}";
|
private static final String FILE_UPLOAD="&module=file&methodName=saveUpload¶ms=objectType={1},objectID={2}&t=json&zentaosid={3}";
|
||||||
private static final String FILE_DELETE="/?m=file&f=delete&t=json&fileID={1}&confirm=yes&zentaosid={2}";
|
private static final String FILE_DELETE="/?m=file&f=delete&t=json&fileID={1}&confirm=yes&zentaosid={2}";
|
||||||
private static final String FILE_DOWNLOAD="/?m=file&f=download&t=json&fileID={1}&mouse=click&zentaosid={2}";
|
private static final String FILE_DOWNLOAD="/?m=file&f=download&t=json&fileID={1}&mouse=click&zentaosid={2}";
|
||||||
private static final String CREATE_META_DATA="?m=bug&f=create&productID={0}&t=json&zentaosid={1}";
|
private static final String CREATE_META_DATA="?m=bug&f=create&productID={0}&t=json&zentaosid={1}";
|
||||||
|
|
Loading…
Reference in New Issue