#20 Improve location checking
This commit is contained in:
@@ -68,6 +68,11 @@
|
|||||||
<!-- <artifactId>spring-security-test</artifactId>-->
|
<!-- <artifactId>spring-security-test</artifactId>-->
|
||||||
<!-- <scope>test</scope>-->
|
<!-- <scope>test</scope>-->
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.nbscloud.files;
|
package de.nbscloud.files;
|
||||||
|
|
||||||
import de.nbscloud.files.config.FilesConfig;
|
import de.nbscloud.files.config.FilesConfig;
|
||||||
import de.nbscloud.files.controller.FilesController;
|
|
||||||
import de.nbscloud.files.exception.FileSystemServiceException;
|
import de.nbscloud.files.exception.FileSystemServiceException;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -89,7 +88,7 @@ public class FileSystemService {
|
|||||||
|
|
||||||
public Path createDirectory(String name) {
|
public Path createDirectory(String name) {
|
||||||
try {
|
try {
|
||||||
return Files.createDirectories(this.locationTracker.getCurrentLocation().resolve(name));
|
return Files.createDirectories(this.locationTracker.resolve(name));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemServiceException("Could not create directory", e);
|
throw new FileSystemServiceException("Could not create directory", e);
|
||||||
}
|
}
|
||||||
@@ -97,8 +96,7 @@ public class FileSystemService {
|
|||||||
|
|
||||||
public void createFile(String name, byte[] content) {
|
public void createFile(String name, byte[] content) {
|
||||||
try {
|
try {
|
||||||
Files.write(this.locationTracker.getCurrentLocation()
|
Files.write(this.locationTracker.resolve(name), content, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
|
||||||
.resolve(name), content, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemServiceException("Could not create file", e);
|
throw new FileSystemServiceException("Could not create file", e);
|
||||||
}
|
}
|
||||||
@@ -112,15 +110,6 @@ public class FileSystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean delete(String name) {
|
|
||||||
try {
|
|
||||||
// TODO does only delete dirs if they are empty - but maybe we want that?
|
|
||||||
return Files.deleteIfExists(this.locationTracker.getCurrentLocation().resolve(name));
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new FileSystemServiceException("Could not delete file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Path move(Path originalPath, Path newPath) {
|
public Path move(Path originalPath, Path newPath) {
|
||||||
try {
|
try {
|
||||||
return Files.move(originalPath, newPath, StandardCopyOption.ATOMIC_MOVE);
|
return Files.move(originalPath, newPath, StandardCopyOption.ATOMIC_MOVE);
|
||||||
@@ -129,9 +118,18 @@ public class FileSystemService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] get(String name) {
|
public boolean delete(String name) {
|
||||||
try {
|
try {
|
||||||
return Files.readAllBytes(this.locationTracker.getCurrentLocation().resolve(name));
|
// TODO does only delete dirs if they are empty - but maybe we want that?
|
||||||
|
return Files.deleteIfExists(this.locationTracker.resolve(name));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new FileSystemServiceException("Could not delete file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] get(Path path) {
|
||||||
|
try {
|
||||||
|
return Files.readAllBytes(path);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemServiceException("Could not get file", e);
|
throw new FileSystemServiceException("Could not get file", e);
|
||||||
}
|
}
|
||||||
@@ -139,7 +137,7 @@ public class FileSystemService {
|
|||||||
|
|
||||||
public void stream(String name, OutputStream outputStream) {
|
public void stream(String name, OutputStream outputStream) {
|
||||||
try {
|
try {
|
||||||
Files.copy(this.locationTracker.getCurrentLocation().resolve(name), outputStream);
|
Files.copy(this.locationTracker.resolve(name), outputStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemServiceException("Could not get file", e);
|
throw new FileSystemServiceException("Could not get file", e);
|
||||||
}
|
}
|
||||||
@@ -147,7 +145,7 @@ public class FileSystemService {
|
|||||||
|
|
||||||
public InputStream stream(String name) {
|
public InputStream stream(String name) {
|
||||||
try {
|
try {
|
||||||
return Files.newInputStream(this.locationTracker.getCurrentLocation().resolve(name));
|
return Files.newInputStream(this.locationTracker.resolve(name));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemServiceException("Could not get file", e);
|
throw new FileSystemServiceException("Could not get file", e);
|
||||||
}
|
}
|
||||||
@@ -155,7 +153,7 @@ public class FileSystemService {
|
|||||||
|
|
||||||
public long getSize(String name) {
|
public long getSize(String name) {
|
||||||
try {
|
try {
|
||||||
return Files.size(this.locationTracker.getCurrentLocation().resolve(name));
|
return Files.size(this.locationTracker.resolve(name));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new FileSystemServiceException("Could not get file", e);
|
throw new FileSystemServiceException("Could not get file", e);
|
||||||
}
|
}
|
||||||
@@ -163,8 +161,7 @@ public class FileSystemService {
|
|||||||
|
|
||||||
public String getMimeType(String name) {
|
public String getMimeType(String name) {
|
||||||
try {
|
try {
|
||||||
final String detectedMimeType = this.mimeTypeDetector.detectMimeType(this.locationTracker.getCurrentLocation()
|
final String detectedMimeType = this.mimeTypeDetector.detectMimeType(this.locationTracker.resolve(name));
|
||||||
.resolve(name));
|
|
||||||
|
|
||||||
logger.debug("Detected mime type {} for file {}", detectedMimeType, name);
|
logger.debug("Detected mime type {} for file {}", detectedMimeType, name);
|
||||||
|
|
||||||
@@ -237,7 +234,7 @@ public class FileSystemService {
|
|||||||
public List<String> collectDirs(String sourceFile) {
|
public List<String> collectDirs(String sourceFile) {
|
||||||
try {
|
try {
|
||||||
final List<String> resultList = new ArrayList<>();
|
final List<String> resultList = new ArrayList<>();
|
||||||
final Path sourcePath = this.locationTracker.getCurrentLocation().resolve(sourceFile);
|
final Path sourcePath = this.locationTracker.resolve(sourceFile);
|
||||||
final boolean sourceIsDir = Files.isDirectory(sourcePath);
|
final boolean sourceIsDir = Files.isDirectory(sourcePath);
|
||||||
|
|
||||||
Files.walkFileTree(this.locationTracker.getBaseDirPath(), new SimpleFileVisitor<Path>() {
|
Files.walkFileTree(this.locationTracker.getBaseDirPath(), new SimpleFileVisitor<Path>() {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.nbscloud.files;
|
package de.nbscloud.files;
|
||||||
|
|
||||||
import de.nbscloud.files.config.FilesConfig;
|
import de.nbscloud.files.config.FilesConfig;
|
||||||
import de.nbscloud.files.controller.FilesController;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -20,6 +19,12 @@ public class LocationTracker {
|
|||||||
private Path baseDirPath;
|
private Path baseDirPath;
|
||||||
private Path currentLocation;
|
private Path currentLocation;
|
||||||
|
|
||||||
|
// For testing
|
||||||
|
void init_internal(String baseDir) {
|
||||||
|
this.baseDirPath = Paths.get(baseDir);
|
||||||
|
this.currentLocation = this.baseDirPath.resolve("");
|
||||||
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
this.baseDirPath = Paths.get(this.filesConfig.getBaseDir());
|
this.baseDirPath = Paths.get(this.filesConfig.getBaseDir());
|
||||||
this.currentLocation = this.baseDirPath.resolve("");
|
this.currentLocation = this.baseDirPath.resolve("");
|
||||||
@@ -33,19 +38,19 @@ public class LocationTracker {
|
|||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getCurrentLocation() {
|
Path getCurrentLocation() {
|
||||||
return this.currentLocation;
|
return this.currentLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getRelativeLocation() {
|
public String getRelativeLocation() {
|
||||||
return this.baseDirPath.relativize(this.currentLocation);
|
return this.baseDirPath.relativize(this.currentLocation).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getRelativeToBaseDir(Path other) {
|
public Path getRelativeToBaseDir(Path other) {
|
||||||
return this.baseDirPath.relativize(other);
|
return this.baseDirPath.relativize(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getTrashBin() {
|
private Path getTrashBin() {
|
||||||
return this.baseDirPath.resolve(this.filesConfig.getTrashBinName());
|
return this.baseDirPath.resolve(this.filesConfig.getTrashBinName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +85,42 @@ public class LocationTracker {
|
|||||||
throw new IllegalStateException("Absolute path: " + navigateTo);
|
throw new IllegalStateException("Absolute path: " + navigateTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.baseDirPath.resolve(navigateTo).startsWith(this.baseDirPath)) {
|
// Regardless of where we navigate to, we must never leave the base directory
|
||||||
|
// The normalize() call is important, as it resolves (possible) relative paths
|
||||||
|
if(!this.currentLocation.resolve(navigateTo).normalize().startsWith(this.baseDirPath)) {
|
||||||
|
throw new IllegalStateException("Illegal path: " + navigateTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!this.baseDirPath.resolve(navigateTo).normalize().startsWith(this.baseDirPath)) {
|
||||||
throw new IllegalStateException("Illegal path: " + navigateTo);
|
throw new IllegalStateException("Illegal path: " + navigateTo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Path getBaseDirPath() {
|
public Path resolve(String name) {
|
||||||
return baseDirPath;
|
validate(name);
|
||||||
|
|
||||||
|
return this.currentLocation.resolve(name).normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path resolveToBaseDir(String name) {
|
||||||
|
validate(name);
|
||||||
|
|
||||||
|
return this.baseDirPath.resolve(name).normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path resolveShare(String shareUuid) {
|
||||||
|
validate(shareUuid);
|
||||||
|
|
||||||
|
return this.baseDirPath.resolve(this.filesConfig.getSharesName()).resolve(shareUuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Path resolveTrash(String name) {
|
||||||
|
validate(name);
|
||||||
|
|
||||||
|
return this.baseDirPath.resolve(this.filesConfig.getTrashBinName()).resolve(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Path getBaseDirPath() {
|
||||||
|
return this.baseDirPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ public class FilesController implements InitializingBean {
|
|||||||
try {
|
try {
|
||||||
// Soft delete
|
// Soft delete
|
||||||
this.fileSystemService.move(
|
this.fileSystemService.move(
|
||||||
this.locationTracker.getCurrentLocation().resolve(Path.of(filename)),
|
this.locationTracker.resolve(filename),
|
||||||
this.locationTracker.getTrashBin().resolve(filename));
|
this.locationTracker.resolveTrash(filename));
|
||||||
|
|
||||||
this.infoMessages.add("nbscloud.files.delete.success");
|
this.infoMessages.add("nbscloud.files.delete.success");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
@@ -114,12 +114,12 @@ public class FilesController implements InitializingBean {
|
|||||||
|
|
||||||
@PostMapping("/files/doRename")
|
@PostMapping("/files/doRename")
|
||||||
public String doRename(RenameForm form) {
|
public String doRename(RenameForm form) {
|
||||||
final Path sourcePath = this.locationTracker.getCurrentLocation().resolve(Path.of(form.getOriginalFilename()));
|
final Path sourcePath = this.locationTracker.resolve(form.getOriginalFilename());
|
||||||
|
|
||||||
// We navigate to the target dir, so we display the content of that dir after the move
|
// We navigate to the target dir, so we display the content of that dir after the move
|
||||||
this.locationTracker.setCurrentLocation(form.getTargetDir().substring(1));
|
this.locationTracker.setCurrentLocation(form.getTargetDir().substring(1));
|
||||||
|
|
||||||
final Path targetPath = this.locationTracker.getCurrentLocation().resolve(Path.of(form.getFilename()));
|
final Path targetPath = this.locationTracker.resolve(form.getFilename());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.fileSystemService.move(sourcePath, targetPath);
|
this.fileSystemService.move(sourcePath, targetPath);
|
||||||
@@ -158,7 +158,7 @@ public class FilesController implements InitializingBean {
|
|||||||
this.fileSystemService.createFile(file.getOriginalFilename(), file.getBytes());
|
this.fileSystemService.createFile(file.getOriginalFilename(), file.getBytes());
|
||||||
|
|
||||||
this.infoMessages.add("nbscloud.files.created.file.success");
|
this.infoMessages.add("nbscloud.files.created.file.success");
|
||||||
} catch (IOException e) {
|
} catch (IOException | RuntimeException e) {
|
||||||
logger.error("Could not upload file", e);
|
logger.error("Could not upload file", e);
|
||||||
|
|
||||||
this.errors.add(e.getMessage());
|
this.errors.add(e.getMessage());
|
||||||
@@ -185,15 +185,11 @@ public class FilesController implements InitializingBean {
|
|||||||
|
|
||||||
@GetMapping("/files/share")
|
@GetMapping("/files/share")
|
||||||
public String share(String filename) {
|
public String share(String filename) {
|
||||||
final Path filePath = this.locationTracker.getRelativeToBaseDir(this.locationTracker.getCurrentLocation()
|
final Path filePath = this.locationTracker.getRelativeToBaseDir(this.locationTracker.resolve(filename));
|
||||||
.resolve(filename));
|
|
||||||
final String shareUuid = UUID.randomUUID().toString();
|
final String shareUuid = UUID.randomUUID().toString();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.fileSystemService.createFile(this.locationTracker.getBaseDirPath()
|
this.fileSystemService.createFile(this.locationTracker.resolveShare(shareUuid), filePath.toString().getBytes(StandardCharsets.UTF_8));
|
||||||
.resolve(this.filesConfig.getSharesName())
|
|
||||||
.resolve(shareUuid), filePath.toString()
|
|
||||||
.getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
this.shareInfo.add("/files/shares?shareUuid=" + shareUuid);
|
this.shareInfo.add("/files/shares?shareUuid=" + shareUuid);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
@@ -209,11 +205,8 @@ public class FilesController implements InitializingBean {
|
|||||||
@GetMapping("files/shares")
|
@GetMapping("files/shares")
|
||||||
public ResponseEntity shares(String shareUuid) {
|
public ResponseEntity shares(String shareUuid) {
|
||||||
try {
|
try {
|
||||||
final String sharedFile = new String(this.fileSystemService.get(this.locationTracker.getBaseDirPath()
|
final String sharedFile = new String(this.fileSystemService.get(this.locationTracker.resolveShare(shareUuid)));
|
||||||
.resolve(this.filesConfig.getSharesName())
|
final Path sharedFilePath = this.locationTracker.resolveToBaseDir(sharedFile);
|
||||||
.resolve(shareUuid)
|
|
||||||
.toString()));
|
|
||||||
final Path sharedFilePath = this.locationTracker.getBaseDirPath().resolve(sharedFile);
|
|
||||||
final String filename = sharedFilePath.getFileName().toString();
|
final String filename = sharedFilePath.getFileName().toString();
|
||||||
|
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
@@ -295,19 +288,19 @@ public class FilesController implements InitializingBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String getCurrentLocation() {
|
private String getCurrentLocation() {
|
||||||
if (this.locationTracker.getRelativeLocation().toString().isEmpty()) {
|
if (this.locationTracker.getRelativeLocation().isEmpty()) {
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.locationTracker.getRelativeLocation().toString();
|
return this.locationTracker.getRelativeLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getCurrentLocationPrefixed() {
|
private String getCurrentLocationPrefixed() {
|
||||||
if (this.locationTracker.getRelativeLocation().toString().isEmpty()) {
|
if (this.locationTracker.getRelativeLocation().isEmpty()) {
|
||||||
return "/";
|
return "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "/" + this.locationTracker.getRelativeLocation().toString();
|
return "/" + this.locationTracker.getRelativeLocation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package de.nbscloud.files;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class LocationTrackerTest {
|
||||||
|
private static final String BASE_DIR = "/tmp";
|
||||||
|
|
||||||
|
private LocationTracker locationTracker = new LocationTracker();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
this.locationTracker.init_internal(BASE_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_null() {
|
||||||
|
this.locationTracker.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_relative1() {
|
||||||
|
this.locationTracker.resolve("../test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_relative2() {
|
||||||
|
this.locationTracker.resolve("/../test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_relative3() {
|
||||||
|
this.locationTracker.resolve("\\../test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_relative4() {
|
||||||
|
this.locationTracker.resolve("\\/../test");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_relative5() {
|
||||||
|
this.locationTracker.resolve("..");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_relative6_ok() {
|
||||||
|
// While 0x2E translates to the dot character and that might be used to
|
||||||
|
// traverse the path, the Java Path API does not resolve those and just treats them as
|
||||||
|
// any other file/dir name
|
||||||
|
Assert.assertTrue(this.locationTracker.resolve("%2e%2e%2f").toString().equals(BASE_DIR + "/%2e%2e%2f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_relative7() {
|
||||||
|
this.locationTracker.resolve("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_relative8_ok() {
|
||||||
|
Assert.assertTrue(this.locationTracker.resolve("././").toString().equals(BASE_DIR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_relative9_ok() {
|
||||||
|
// The tilde is treated as regular file/dir name if it is resolved from a path
|
||||||
|
Assert.assertTrue(this.locationTracker.resolve("~").toString().equals(BASE_DIR + "/~"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void test_absolut() {
|
||||||
|
this.locationTracker.resolve("/bin");
|
||||||
|
}
|
||||||
|
}
|
||||||
11
pom.xml
11
pom.xml
@@ -1,7 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-parent</artifactId>
|
<artifactId>spring-boot-starter-parent</artifactId>
|
||||||
@@ -114,6 +113,13 @@
|
|||||||
<artifactId>mime-types</artifactId>
|
<artifactId>mime-types</artifactId>
|
||||||
<version>1.0.2</version>
|
<version>1.0.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
@@ -193,5 +199,4 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
</project>
|
||||||
</project>
|
|
||||||
Reference in New Issue
Block a user