|
|
@@ -18,7 +18,9 @@ import com.storlead.sales.mail.enums.SendStatus;
|
|
|
import com.storlead.sales.mail.pojo.*;
|
|
|
import com.storlead.sales.mail.properties.MailFileProperties;
|
|
|
import com.storlead.sales.mail.service.*;
|
|
|
+import com.storlead.sales.mail.support.MailOwnershipGuard;
|
|
|
import com.storlead.sales.mail.util.FileHelper;
|
|
|
+import com.storlead.sales.mail.util.SafeFilePathResolver;
|
|
|
import com.storlead.sales.mail.util.ZipUtility;
|
|
|
import io.swagger.annotations.Api;
|
|
|
import io.swagger.annotations.ApiOperation;
|
|
|
@@ -85,6 +87,9 @@ public class MailApiController {
|
|
|
@Resource
|
|
|
private Environment environment;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private MailOwnershipGuard mailOwnershipGuard;
|
|
|
+
|
|
|
@PostMapping(value = "/send_mail")
|
|
|
@ApiOperation(value = "发送邮件")
|
|
|
@ApiResponses({
|
|
|
@@ -149,6 +154,9 @@ public class MailApiController {
|
|
|
entity.setRecipientBcc(org.apache.commons.lang3.StringUtils.join(dto.getRecipientBccls(), ","));
|
|
|
}
|
|
|
if (Objects.nonNull(dto.getId())) {
|
|
|
+ if (mailOwnershipGuard.getOwnedEmail(dto.getId(), currentUserId) == null) {
|
|
|
+ return Result.error("无权操作该邮件");
|
|
|
+ }
|
|
|
entity.setId(dto.getId());
|
|
|
}
|
|
|
|
|
|
@@ -162,12 +170,22 @@ public class MailApiController {
|
|
|
|
|
|
emailsService.saveOrUpdate(entity);
|
|
|
if (!CollectionUtils.isEmpty(dto.getFileIds())) {
|
|
|
+ for (Long fileId : dto.getFileIds()) {
|
|
|
+ if (mailOwnershipGuard.getOwnedTempAttachment(fileId, currentUserId) == null) {
|
|
|
+ return Result.error("无权操作附件");
|
|
|
+ }
|
|
|
+ }
|
|
|
LambdaUpdateWrapper<MailTempAttachmentEntity> fileWrapper = new LambdaUpdateWrapper<>();
|
|
|
fileWrapper.set(MailTempAttachmentEntity::getEmailId, entity.getId());
|
|
|
fileWrapper.in(MailTempAttachmentEntity::getId, dto.getFileIds());
|
|
|
tempAttachmentService.update(fileWrapper);
|
|
|
}
|
|
|
if (!CollectionUtils.isEmpty(dto.getOldfileIds())) {
|
|
|
+ for (Long fileId : dto.getOldfileIds()) {
|
|
|
+ if (mailOwnershipGuard.getOwnedAttachment(fileId, currentUserId) == null) {
|
|
|
+ return Result.error("无权操作附件");
|
|
|
+ }
|
|
|
+ }
|
|
|
LambdaUpdateWrapper<MailAttachmentEntity> fileWrapper = new LambdaUpdateWrapper<>();
|
|
|
fileWrapper.in(MailAttachmentEntity::getId, dto.getOldfileIds());
|
|
|
List<MailAttachmentEntity> mailAttachments = mailAttachmentService.list(fileWrapper);
|
|
|
@@ -217,7 +235,7 @@ public class MailApiController {
|
|
|
if (CollectionUtils.isEmpty(dto.getMailIds())) {
|
|
|
return;
|
|
|
}
|
|
|
- List<EmailsEntity> entities = emailsService.list(new LambdaQueryWrapper<EmailsEntity>().in(EmailsEntity::getId, dto.getMailIds()));
|
|
|
+ List<EmailsEntity> entities = mailOwnershipGuard.listOwnedEmails(dto.getMailIds(), currentUserId);
|
|
|
if (CollectionUtils.isEmpty(entities)) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -285,7 +303,8 @@ public class MailApiController {
|
|
|
if (Objects.isNull(dto.getMailId())) {
|
|
|
return;
|
|
|
}
|
|
|
- EmailsEntity entitie = emailsService.getById(dto.getMailId());
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ EmailsEntity entitie = mailOwnershipGuard.getOwnedEmail(dto.getMailId(), currentUserId);
|
|
|
if (Objects.isNull(entitie)) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -402,7 +421,7 @@ public class MailApiController {
|
|
|
// 设置文本消息部分
|
|
|
for (MailTempAttachmentEntity attachment : tempAttachment) {
|
|
|
BodyPart messageBodyPart = new MimeBodyPart();
|
|
|
- String filePath = mailFileProperties.getPath().getPath() + attachment.getFilePath();
|
|
|
+ String filePath = resolveMailFile(attachment.getFilePath()).toString();
|
|
|
messageBodyPart = new MimeBodyPart();
|
|
|
String fileName = attachment.getFileName() + "." + attachment.getFileExt();
|
|
|
DataSource fileSource = new FileDataSource(filePath);
|
|
|
@@ -479,9 +498,11 @@ public class MailApiController {
|
|
|
if (Objects.isNull(ids)) {
|
|
|
return Result.error("参数错误");
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
LambdaUpdateWrapper<EmailsEntity> uWrapper = new LambdaUpdateWrapper<>();
|
|
|
uWrapper.set(EmailsEntity::getIsDelete, Integer.valueOf(1));
|
|
|
uWrapper.in(EmailsEntity::getId, Arrays.asList(ids));
|
|
|
+ uWrapper.eq(EmailsEntity::getOwnerBy, currentUserId);
|
|
|
emailsService.update(uWrapper);
|
|
|
return Result.ok();
|
|
|
}
|
|
|
@@ -495,8 +516,10 @@ public class MailApiController {
|
|
|
if (Objects.isNull(ids)) {
|
|
|
return Result.error("删除草稿");
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
LambdaUpdateWrapper<EmailsEntity> uWrapper = new LambdaUpdateWrapper<>();
|
|
|
uWrapper.in(EmailsEntity::getId, Arrays.asList(ids));
|
|
|
+ uWrapper.eq(EmailsEntity::getOwnerBy, currentUserId);
|
|
|
emailsService.remove(uWrapper);
|
|
|
return Result.ok();
|
|
|
}
|
|
|
@@ -510,8 +533,12 @@ public class MailApiController {
|
|
|
if (Objects.isNull(mailId)) {
|
|
|
return Result.error("参数错误!");
|
|
|
}
|
|
|
- EmailsEntity email = emailsService.getById(mailId);
|
|
|
- if (Objects.nonNull(email) && EmailBoxEnum.DRAFT.code.equals(email.getFolder())) {
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ EmailsEntity email = mailOwnershipGuard.getOwnedEmail(mailId, currentUserId);
|
|
|
+ if (email == null) {
|
|
|
+ return Result.error("邮件不存在或无权访问");
|
|
|
+ }
|
|
|
+ if (EmailBoxEnum.DRAFT.code.equals(email.getFolder())) {
|
|
|
List<MailTempAttachmentEntity> atts = tempAttachmentService.list(new LambdaQueryWrapper<MailTempAttachmentEntity>().eq(MailTempAttachmentEntity::getEmailId, mailId));
|
|
|
if (!CollectionUtils.isEmpty(atts)) {
|
|
|
atts.forEach(e -> {
|
|
|
@@ -538,9 +565,17 @@ public class MailApiController {
|
|
|
if (Objects.isNull(mailIds) || Objects.isNull(folderId)) {
|
|
|
return Result.error("参数错误");
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ if (mailOwnershipGuard.getOwnedFolder(folderId, currentUserId) == null) {
|
|
|
+ return Result.error("文件夹不存在或无权访问");
|
|
|
+ }
|
|
|
+ if (!mailOwnershipGuard.ownsAllEmails(Arrays.asList(mailIds), currentUserId)) {
|
|
|
+ return Result.error("无权操作邮件");
|
|
|
+ }
|
|
|
LambdaUpdateWrapper<EmailsEntity> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
updateWrapper.set(EmailsEntity::getCustomFolderId, folderId);
|
|
|
updateWrapper.in(EmailsEntity::getId, Arrays.asList(mailIds));
|
|
|
+ updateWrapper.eq(EmailsEntity::getOwnerBy, currentUserId);
|
|
|
emailsService.update(updateWrapper);
|
|
|
return Result.ok();
|
|
|
}
|
|
|
@@ -551,8 +586,10 @@ public class MailApiController {
|
|
|
if (Objects.isNull(mailIds)) {
|
|
|
return Result.error("参数错误");
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
LambdaUpdateWrapper<EmailsEntity> updateWrapper = new LambdaUpdateWrapper<>();
|
|
|
updateWrapper.in(EmailsEntity::getId, Arrays.asList(mailIds));
|
|
|
+ updateWrapper.eq(EmailsEntity::getOwnerBy, currentUserId);
|
|
|
updateWrapper.set(EmailsEntity::getCustomFolderId, null);
|
|
|
emailsService.update(updateWrapper);
|
|
|
return Result.ok();
|
|
|
@@ -589,8 +626,10 @@ public class MailApiController {
|
|
|
if (Objects.isNull(dto.getEmailId()) && StringUtils.isEmpty(dto.getEmailIds())) {
|
|
|
return Result.error("参数错误!");
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
LambdaUpdateWrapper<EmailsEntity> uWrapper = new LambdaUpdateWrapper<>();
|
|
|
uWrapper.set(EmailsEntity::getIsRead, dto.getIsRead());
|
|
|
+ uWrapper.eq(EmailsEntity::getOwnerBy, currentUserId);
|
|
|
if (Objects.nonNull(dto.getEmailId())) {
|
|
|
uWrapper.eq(EmailsEntity::getId, dto.getEmailId());
|
|
|
} else {
|
|
|
@@ -609,8 +648,10 @@ public class MailApiController {
|
|
|
if (Objects.isNull(dto.getEmailId()) && StringUtils.isEmpty(dto.getEmailIds())) {
|
|
|
return Result.error("参数错误!");
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
LambdaUpdateWrapper<EmailsEntity> uWrapper = new LambdaUpdateWrapper<>();
|
|
|
uWrapper.set(EmailsEntity::getReplyMsgId, dto.getReplyMsgId());
|
|
|
+ uWrapper.eq(EmailsEntity::getOwnerBy, currentUserId);
|
|
|
if (Objects.nonNull(dto.getEmailId())) {
|
|
|
uWrapper.eq(EmailsEntity::getId, dto.getEmailId());
|
|
|
} else {
|
|
|
@@ -704,12 +745,19 @@ public class MailApiController {
|
|
|
if (Objects.isNull(dto.getMailId()) && CollectionUtils.isEmpty(dto.getAttachmentIds())) {
|
|
|
return;
|
|
|
}
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
String zipFilePath = mailFileProperties.getPath().getPath() + System.currentTimeMillis() + RandomGenerateHelper.generateRandomString(6) + ".zip";
|
|
|
List<MailAttachmentEntity> attachments;
|
|
|
if (Objects.nonNull(dto.getMailId())) {
|
|
|
+ if (mailOwnershipGuard.getOwnedEmail(dto.getMailId(), currentUserId) == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
attachments = mailAttachmentService.list(new LambdaQueryWrapper<MailAttachmentEntity>().eq(MailAttachmentEntity::getEmailId, dto.getMailId()).eq(MailAttachmentEntity::getDownload, Integer.valueOf(1)));
|
|
|
} else {
|
|
|
attachments = mailAttachmentService.list(new LambdaQueryWrapper<MailAttachmentEntity>().in(MailAttachmentEntity::getId, dto.getAttachmentIds()).eq(MailAttachmentEntity::getDownload, Integer.valueOf(1)));
|
|
|
+ attachments = attachments.stream()
|
|
|
+ .filter(a -> mailOwnershipGuard.getOwnedAttachment(a.getId(), currentUserId) != null)
|
|
|
+ .collect(Collectors.toList());
|
|
|
}
|
|
|
if (CollectionUtils.isEmpty(attachments)) {
|
|
|
return;
|
|
|
@@ -754,36 +802,33 @@ public class MailApiController {
|
|
|
if (attachmentId == null) {
|
|
|
return Result.error("附未找到");
|
|
|
}
|
|
|
- MailAttachmentEntity attachment = mailAttachmentService.getById(attachmentId);
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ MailAttachmentEntity attachment = mailOwnershipGuard.getOwnedAttachment(attachmentId, currentUserId);
|
|
|
if (attachment == null) {
|
|
|
return Result.error("附未找到");
|
|
|
}
|
|
|
|
|
|
- String url = mailFileProperties.getPath().getPath() + File.separator + attachment.getFilePath();
|
|
|
-
|
|
|
+ Path filePath = resolveMailFile(attachment.getFilePath());
|
|
|
+ if (StrUtil.isNotBlank(attachment.getFileCode())) {
|
|
|
+ String alt = attachment.getFilePath().replace("." + attachment.getFileCode(), "");
|
|
|
+ if (!alt.equals(attachment.getFilePath())) {
|
|
|
+ filePath = resolveMailFile(alt);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // String newFileName = attachment.getFilePath().replace("."+attachment.getFileCode(),"");
|
|
|
File exportDir;
|
|
|
String active = environment.getProperty("spring.profiles.active");
|
|
|
String port = environment.getProperty("server.port");
|
|
|
if (active.equals("dev")) {
|
|
|
exportDir = new File("D:\\mail\\tempfiles");
|
|
|
} else {
|
|
|
- url = url.replace("\\", "/");
|
|
|
exportDir = new File("/app/tempfiles");
|
|
|
}
|
|
|
- Path filePath = Paths.get(url);
|
|
|
if (!exportDir.exists()) {
|
|
|
exportDir.mkdirs();
|
|
|
}
|
|
|
|
|
|
- Path targetPath1;
|
|
|
- if (StrUtil.isNotBlank(attachment.getFileCode())) {
|
|
|
- targetPath1 = Paths.get(url.replace("."+attachment.getFileCode(),""));
|
|
|
- } else {
|
|
|
- targetPath1 = Paths.get(url);
|
|
|
- }
|
|
|
- String fileName = targetPath1.getFileName().toString();
|
|
|
+ String fileName = filePath.getFileName().toString();
|
|
|
|
|
|
Path target = Paths.get(exportDir.getAbsolutePath());
|
|
|
// 创建目标目录(如果不存在)
|
|
|
@@ -805,6 +850,9 @@ public class MailApiController {
|
|
|
domainUrl = "http://localhost:"+port+fileUrl;
|
|
|
}
|
|
|
return Result.result(domainUrl);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ log.warn("download_url rejected unsafe path, attachmentId={}", attachmentId);
|
|
|
+ return Result.error("附未找到");
|
|
|
} catch (Exception e) {
|
|
|
log.error("download--error", e);
|
|
|
return Result.error("附未找到");
|
|
|
@@ -818,12 +866,19 @@ public class MailApiController {
|
|
|
if (attachmentId == null) {
|
|
|
return Result.error("附未找到");
|
|
|
}
|
|
|
- MailTempAttachmentEntity attachment = tempAttachmentService.getById(attachmentId);
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ MailTempAttachmentEntity attachment = mailOwnershipGuard.getOwnedTempAttachment(attachmentId, currentUserId);
|
|
|
if (attachment == null) {
|
|
|
return Result.error("附未找到");
|
|
|
}
|
|
|
|
|
|
- String url = mailFileProperties.getPath().getPath() + File.separator + attachment.getFilePath();
|
|
|
+ Path filePath = resolveMailFile(attachment.getFilePath());
|
|
|
+ if (StrUtil.isNotBlank(attachment.getFileCode())) {
|
|
|
+ String alt = attachment.getFilePath().replace("." + attachment.getFileCode(), "");
|
|
|
+ if (!alt.equals(attachment.getFilePath())) {
|
|
|
+ filePath = resolveMailFile(alt);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
File exportDir;
|
|
|
String active = environment.getProperty("spring.profiles.active");
|
|
|
@@ -831,20 +886,12 @@ public class MailApiController {
|
|
|
if (active.equals("dev")) {
|
|
|
exportDir = new File("D:\\mail\\tempfiles");
|
|
|
} else {
|
|
|
- url = url.replace("\\", "/");
|
|
|
exportDir = new File("/app/tempfiles");
|
|
|
}
|
|
|
- Path filePath = Paths.get(url);
|
|
|
if (!exportDir.exists()) {
|
|
|
exportDir.mkdirs();
|
|
|
}
|
|
|
- Path targetPath1;
|
|
|
- if (StrUtil.isNotBlank(attachment.getFileCode())) {
|
|
|
- targetPath1 = Paths.get(url.replace("."+attachment.getFileCode(),""));
|
|
|
- } else {
|
|
|
- targetPath1 = Paths.get(url);
|
|
|
- }
|
|
|
- String fileName = targetPath1.getFileName().toString();
|
|
|
+ String fileName = filePath.getFileName().toString();
|
|
|
|
|
|
Path target = Paths.get(exportDir.getAbsolutePath());
|
|
|
// 创建目标目录(如果不存在)
|
|
|
@@ -866,6 +913,9 @@ public class MailApiController {
|
|
|
domainUrl = "http://localhost:"+port+fileUrl;
|
|
|
}
|
|
|
return Result.result(domainUrl);
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ log.warn("draft_download_url rejected unsafe path, attachmentId={}", attachmentId);
|
|
|
+ return Result.error("附未找到");
|
|
|
} catch (Exception e) {
|
|
|
log.error("download--error", e);
|
|
|
return Result.error("附未找到");
|
|
|
@@ -879,13 +929,13 @@ public class MailApiController {
|
|
|
if (attachmentId == null) {
|
|
|
return;
|
|
|
}
|
|
|
- MailTempAttachmentEntity attachment = tempAttachmentService.getById(attachmentId);
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ MailTempAttachmentEntity attachment = mailOwnershipGuard.getOwnedTempAttachment(attachmentId, currentUserId);
|
|
|
if (attachment == null) {
|
|
|
return;
|
|
|
}
|
|
|
- InputStream inputStream;
|
|
|
- String url = mailFileProperties.getPath().getPath() + attachment.getFilePath();
|
|
|
- inputStream = new BufferedInputStream(new FileInputStream(url));
|
|
|
+ Path filePath = resolveMailFile(attachment.getFilePath());
|
|
|
+ InputStream inputStream = new BufferedInputStream(new FileInputStream(filePath.toFile()));
|
|
|
response.reset();
|
|
|
response.setContentType("application/octet-stream;charset=utf-8");
|
|
|
/*
|
|
|
@@ -909,6 +959,8 @@ public class MailApiController {
|
|
|
}
|
|
|
inputStream.close();
|
|
|
outputStream.close();
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ log.warn("draft_download rejected unsafe path, attachmentId={}", attachmentId);
|
|
|
} catch (Exception e) {
|
|
|
log.error("download--error", e);
|
|
|
}
|
|
|
@@ -921,14 +973,16 @@ public class MailApiController {
|
|
|
if (attachmentId == null) {
|
|
|
return;
|
|
|
}
|
|
|
- MailAttachmentEntity attachment = mailAttachmentService.getById(attachmentId);
|
|
|
+ Long currentUserId = LoginUserUtil.getCurrentUserId();
|
|
|
+ MailAttachmentEntity attachment = mailOwnershipGuard.getOwnedAttachment(attachmentId, currentUserId);
|
|
|
if (attachment == null) {
|
|
|
return;
|
|
|
}
|
|
|
InputStream inputStream;
|
|
|
- String url = mailFileProperties.getPath().getPath() + attachment.getFilePath();
|
|
|
+ Path filePath = resolveMailFile(attachment.getFilePath());
|
|
|
+ String url = filePath.toString();
|
|
|
if (Integer.valueOf(0).equals(attachment.getDownload())) {
|
|
|
- if (new File(url).exists()) {
|
|
|
+ if (Files.exists(filePath)) {
|
|
|
LambdaUpdateWrapper<MailAttachmentEntity> attachmentUpdate = new LambdaUpdateWrapper<>();
|
|
|
attachmentUpdate.set(MailAttachmentEntity::getDownload, Integer.valueOf(1));
|
|
|
attachmentUpdate.eq(MailAttachmentEntity::getId, attachment.getId());
|
|
|
@@ -941,8 +995,7 @@ public class MailApiController {
|
|
|
if (Objects.isNull(inputStream)) {
|
|
|
return;
|
|
|
}
|
|
|
- String path = mailFileProperties.getPath().getPath() + attachment.getFilePath();
|
|
|
- File file = new File(path);
|
|
|
+ File file = filePath.toFile();
|
|
|
try (FileOutputStream output = new FileOutputStream(file)) {
|
|
|
inputStream.transferTo(output);
|
|
|
}
|
|
|
@@ -998,6 +1051,8 @@ public class MailApiController {
|
|
|
}
|
|
|
inputStream.close();
|
|
|
outputStream.close();
|
|
|
+ } catch (IllegalArgumentException e) {
|
|
|
+ log.warn("download rejected unsafe path, attachmentId={}", attachmentId);
|
|
|
} catch (Exception e) {
|
|
|
log.error("download--error", e);
|
|
|
}
|
|
|
@@ -1064,4 +1119,8 @@ public class MailApiController {
|
|
|
return tempFilePath;
|
|
|
}
|
|
|
|
|
|
+ private Path resolveMailFile(String relativePath) {
|
|
|
+ return SafeFilePathResolver.resolveUnderBase(mailFileProperties.getPath().getPath(), relativePath);
|
|
|
+ }
|
|
|
+
|
|
|
}
|