mirror of https://github.com/halo-dev/halo
* Add rename API * Add save API * Add unit test for rename API * Fix an indentation * Add a space before '{' * Add a Param and some invalid checks * Change comments * Change test annotation * Delete impl test unit * Add directory traversal check for static file operations Co-authored-by: Rokita <35868425+Ruukita@users.noreply.github.com>pull/838/head
parent
a81f335ee8
commit
4197ca33d8
|
@ -3,6 +3,7 @@ package run.halo.app.controller.admin.api;
|
|||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import run.halo.app.model.params.StaticContentParam;
|
||||
import run.halo.app.model.support.StaticFile;
|
||||
import run.halo.app.service.StaticStorageService;
|
||||
|
||||
|
@ -49,4 +50,17 @@ public class StaticStorageController {
|
|||
@RequestPart("file") MultipartFile file) {
|
||||
staticStorageService.upload(basePath, file);
|
||||
}
|
||||
|
||||
@PostMapping("rename")
|
||||
@ApiOperation("Renames static file")
|
||||
public void rename(String basePath,
|
||||
String newName) {
|
||||
staticStorageService.rename(basePath, newName);
|
||||
}
|
||||
|
||||
@PutMapping("files")
|
||||
@ApiOperation("Save static file")
|
||||
public void save(@RequestBody StaticContentParam param) {
|
||||
staticStorageService.save(param.getPath(), param.getContent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package run.halo.app.handler.migrate.support.wordpress;
|
|||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import run.halo.app.handler.migrate.utils.PropertyMappingTo;
|
||||
import run.halo.app.model.entity.BasePost;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package run.halo.app.model.params;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* Static content param.
|
||||
*
|
||||
* @author Holldean
|
||||
* @date 2020-05-04
|
||||
*/
|
||||
@Data
|
||||
public class StaticContentParam {
|
||||
private String path;
|
||||
private String content;
|
||||
}
|
|
@ -50,4 +50,20 @@ public interface StaticStorageService {
|
|||
* @param file file must not be null.
|
||||
*/
|
||||
void upload(String basePath, @NonNull MultipartFile file);
|
||||
|
||||
/**
|
||||
* Rename static file or folder.
|
||||
*
|
||||
* @param basePath base path must not be null
|
||||
* @param newName new name must not be null
|
||||
*/
|
||||
void rename(@NonNull String basePath, @NonNull String newName);
|
||||
|
||||
/**
|
||||
* Save static file.
|
||||
*
|
||||
* @param path path must not be null
|
||||
* @param content saved content
|
||||
*/
|
||||
void save(@NonNull String path, String content);
|
||||
}
|
||||
|
|
|
@ -21,9 +21,8 @@ import run.halo.app.utils.FileUtils;
|
|||
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -97,6 +96,10 @@ public class StaticStorageServiceImpl implements StaticStorageService, Applicati
|
|||
Assert.notNull(relativePath, "Relative path must not be null");
|
||||
|
||||
Path path = Paths.get(staticDir.toString(), relativePath);
|
||||
|
||||
// check if the path is valid (not outside staticDir)
|
||||
FileUtils.checkDirectoryTraversal(staticDir.toString(), path.toString());
|
||||
|
||||
log.debug(path.toString());
|
||||
|
||||
try {
|
||||
|
@ -127,6 +130,9 @@ public class StaticStorageServiceImpl implements StaticStorageService, Applicati
|
|||
path = Paths.get(staticDir.toString(), basePath, folderName);
|
||||
}
|
||||
|
||||
// check if the path is valid (not outside staticDir)
|
||||
FileUtils.checkDirectoryTraversal(staticDir.toString(), path.toString());
|
||||
|
||||
if (path.toFile().exists()) {
|
||||
throw new FileOperationException("目录 " + path.toString() + " 已存在").setErrorData(path);
|
||||
}
|
||||
|
@ -154,6 +160,9 @@ public class StaticStorageServiceImpl implements StaticStorageService, Applicati
|
|||
uploadPath = Paths.get(staticDir.toString(), basePath, file.getOriginalFilename());
|
||||
}
|
||||
|
||||
// check if the path is valid (not outside staticDir)
|
||||
FileUtils.checkDirectoryTraversal(staticDir.toString(), uploadPath.toString());
|
||||
|
||||
if (uploadPath.toFile().exists()) {
|
||||
throw new FileOperationException("文件 " + file.getOriginalFilename() + " 已存在").setErrorData(uploadPath);
|
||||
}
|
||||
|
@ -167,6 +176,53 @@ public class StaticStorageServiceImpl implements StaticStorageService, Applicati
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rename(String basePath, String newName) {
|
||||
Assert.notNull(basePath, "Base path must not be null");
|
||||
Assert.notNull(newName, "New name must not be null");
|
||||
|
||||
Path pathToRename;
|
||||
|
||||
if (StringUtils.startsWith(newName, API_FOLDER_NAME)) {
|
||||
throw new FileOperationException("重命名名称 " + newName + " 不合法");
|
||||
}
|
||||
|
||||
pathToRename = Paths.get(staticDir.toString(), basePath);
|
||||
|
||||
// check if the path is valid (not outside staticDir)
|
||||
FileUtils.checkDirectoryTraversal(staticDir.toString(), pathToRename.toString());
|
||||
|
||||
try {
|
||||
FileUtils.rename(pathToRename, newName);
|
||||
onChange();
|
||||
} catch (FileAlreadyExistsException e) {
|
||||
throw new FileOperationException("该路径下名称 " + newName + " 已存在");
|
||||
} catch (IOException e) {
|
||||
throw new FileOperationException("重命名 " + pathToRename.toString() + " 失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(String path, String content) {
|
||||
Assert.notNull(path, "Path must not be null");
|
||||
|
||||
Path savePath = Paths.get(staticDir.toString(), path);
|
||||
|
||||
// check if the path is valid (not outside staticDir)
|
||||
FileUtils.checkDirectoryTraversal(staticDir.toString(), savePath.toString());
|
||||
|
||||
// check if file exist
|
||||
if (!Files.isRegularFile(savePath)) {
|
||||
throw new FileOperationException("路径 " + path + " 不合法");
|
||||
}
|
||||
|
||||
try {
|
||||
Files.write(savePath, content.getBytes(StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
throw new ServiceException("保存内容失败 " + path, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onChange() {
|
||||
eventPublisher.publishEvent(new StaticStorageChangedEvent(this, staticDir));
|
||||
}
|
||||
|
|
|
@ -77,6 +77,24 @@ public class FileUtils {
|
|||
log.info("Deleted [{}] successfully", deletingPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames file or folder.
|
||||
*
|
||||
* @param pathToRename file path to rename must not be null
|
||||
* @param newName new name must not be null
|
||||
*/
|
||||
public static void rename(@NonNull Path pathToRename, @NonNull String newName) throws IOException {
|
||||
Assert.notNull(pathToRename, "File path to rename must not be null");
|
||||
Assert.notNull(newName, "New name must not be null");
|
||||
|
||||
Path newPath = pathToRename.resolveSibling(newName);
|
||||
log.info("Rename [{}] to [{}]", pathToRename, newPath);
|
||||
|
||||
Files.move(pathToRename, newPath);
|
||||
|
||||
log.info("Rename [{}] successfully", pathToRename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unzips content to the target path.
|
||||
*
|
||||
|
|
|
@ -8,6 +8,7 @@ import run.halo.app.model.support.HaloConst;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
@ -108,4 +109,90 @@ public class FileUtilsTest {
|
|||
log.debug("Buffer String: [{}]", bufString);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameFile() throws IOException {
|
||||
// Create a temp folder
|
||||
Path tempDirectory = Files.createTempDirectory("halo-test");
|
||||
|
||||
Path testPath = tempDirectory.resolve("test/test");
|
||||
Path filePath = tempDirectory.resolve("test/test/test.file");
|
||||
|
||||
// Create a temp file and folder
|
||||
Files.createDirectories(testPath);
|
||||
Files.createFile(filePath);
|
||||
|
||||
// Write content to the temp file
|
||||
String content = "Test Content!\n";
|
||||
Files.write(filePath, content.getBytes());
|
||||
|
||||
// Rename temp file
|
||||
FileUtils.rename(filePath, "newName");
|
||||
Path newPath = filePath.resolveSibling("newName");
|
||||
|
||||
Assert.assertFalse(Files.exists(filePath));
|
||||
Assert.assertTrue(Files.isRegularFile(newPath));
|
||||
Assert.assertEquals(new String(Files.readAllBytes(newPath)), content);
|
||||
|
||||
FileUtils.deleteFolder(tempDirectory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameFolder() throws IOException {
|
||||
// Create a temp folder
|
||||
Path tempDirectory = Files.createTempDirectory("halo-test");
|
||||
|
||||
Path testPath = tempDirectory.resolve("test/test");
|
||||
Path filePath = tempDirectory.resolve("test/test.file");
|
||||
|
||||
// Create a temp file and folder
|
||||
Files.createDirectories(testPath);
|
||||
Files.createFile(filePath);
|
||||
|
||||
// Rename temp folder
|
||||
FileUtils.rename(tempDirectory.resolve("test"), "newName");
|
||||
Path newPath = tempDirectory.resolve("newName");
|
||||
|
||||
Assert.assertTrue(Files.isDirectory(newPath));
|
||||
Assert.assertTrue(Files.isRegularFile(newPath.resolve("test.file")));
|
||||
|
||||
FileUtils.deleteFolder(tempDirectory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRenameRepeat() throws IOException {
|
||||
// Create a temp folder
|
||||
Path tempDirectory = Files.createTempDirectory("halo-test");
|
||||
|
||||
Path testPathOne = tempDirectory.resolve("test/testOne");
|
||||
Path testPathTwo = tempDirectory.resolve("test/testTwo");
|
||||
Path filePathOne = tempDirectory.resolve("test/testOne.file");
|
||||
Path filePathTwo = tempDirectory.resolve("test/testTwo.file");
|
||||
|
||||
// Create temp files and folders
|
||||
Files.createDirectories(testPathOne);
|
||||
Files.createDirectories(testPathTwo);
|
||||
Files.createFile(filePathOne);
|
||||
Files.createFile(filePathTwo);
|
||||
|
||||
try {
|
||||
FileUtils.rename(testPathOne, "testTwo");
|
||||
} catch (Exception e) {
|
||||
Assert.assertTrue(e instanceof FileAlreadyExistsException);
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.rename(filePathOne, "testTwo.file");
|
||||
} catch (Exception e) {
|
||||
Assert.assertTrue(e instanceof FileAlreadyExistsException);
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.rename(filePathOne, "testOne");
|
||||
} catch (Exception e) {
|
||||
Assert.assertTrue(e instanceof FileAlreadyExistsException);
|
||||
}
|
||||
|
||||
FileUtils.deleteFolder(tempDirectory);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue