1
0

Basic implementation of files API that allows other apps filesystem access

This commit is contained in:
2022-08-13 18:40:13 +02:00
parent bd6f1e43d1
commit a0239ecda6
13 changed files with 237 additions and 34 deletions

View File

@@ -21,6 +21,10 @@
<groupId>de.77zzcx7.nbs-cloud</groupId>
<artifactId>web-container-config</artifactId>
</dependency>
<dependency>
<groupId>de.77zzcx7.nbs-cloud</groupId>
<artifactId>files-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -0,0 +1,54 @@
package de.nbscloud.files;
import de.nbscloud.files.config.FilesConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.file.Path;
@Component
public class AppLocationTracker implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(AppLocationTracker.class);
@Autowired
private FilesConfig filesConfig;
@Autowired
private LocationTracker locationTracker;
private Path baseDirPath;
public Path resolve(String appId, Path path) {
validate(Path.of(appId));
validate(path);
final Path appPath = this.baseDirPath.resolve(appId);
return appPath.resolve(path);
}
private void validate(Path path) {
if(path == null) {
throw new IllegalStateException("Null");
}
if(path.toString().contains("..")) {
throw new IllegalStateException("Relative path");
}
if (path.isAbsolute()) {
throw new IllegalStateException("Absolute path: " + path);
}
if(!this.baseDirPath.resolve(path).normalize().startsWith(this.baseDirPath)) {
throw new IllegalStateException("Illegal path: " + path);
}
}
@Override
public void afterPropertiesSet() throws Exception {
this.baseDirPath = this.locationTracker.getAppDir();
}
}

View File

@@ -1,5 +1,6 @@
package de.nbscloud.files;
import de.nbscloud.files.api.FilesService.ContentContainer;
import de.nbscloud.files.config.FilesConfig;
import de.nbscloud.files.exception.FileSystemServiceException;
import org.apache.commons.io.FileUtils;
@@ -20,6 +21,7 @@ import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -29,10 +31,6 @@ public class FileSystemService {
private static final Logger logger = LoggerFactory.getLogger(FileSystemService.class);
public static record ContentContainer(boolean directory, Path path, String name, long size,
LocalDateTime lastModified) {
}
public enum SortOrder {
/**
* First by type (directories first), then by name ignoring case
@@ -95,6 +93,14 @@ public class FileSystemService {
}
}
void createDirectory(Path path) {
try {
Files.createDirectories(path);
} catch (IOException e) {
throw new FileSystemServiceException("Could not create directory", e);
}
}
public void createFile(String name, byte[] content) {
try {
Files.write(this.locationTracker.resolve(name), content, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
@@ -278,7 +284,7 @@ public class FileSystemService {
}
}
public List<ContentContainer> list(SortOrder sortOrder) {
List<ContentContainer> list(Path startPath, SortOrder sortOrder, Function<Path, Path> relativizer) {
try {
List<ContentContainer> contentList = Files.list(this.locationTracker.getCurrentLocation())
.filter(path -> {
@@ -295,7 +301,7 @@ public class FileSystemService {
final boolean isDir = Files.isDirectory(path);
return new ContentContainer(isDir,
this.locationTracker.getRelativeToBaseDir(path),
relativizer.apply(path),
path.getFileName().toString(),
isDir ? FileUtils.sizeOfDirectory(path.toFile()) : Files.size(path),
LocalDateTime.ofInstant(Files.getLastModifiedTime(path)
@@ -313,6 +319,10 @@ public class FileSystemService {
}
}
public List<ContentContainer> list(SortOrder sortOrder) {
return list(this.locationTracker.getCurrentLocation(), sortOrder, path -> this.locationTracker.getRelativeToBaseDir(path));
}
public List<String> collectDirs(String sourceFile) {
try {
final List<String> resultList = new ArrayList<>();

View File

@@ -0,0 +1,61 @@
package de.nbscloud.files;
import de.nbscloud.files.api.FilesService;
import de.nbscloud.files.exception.FileSystemServiceException;
import de.nbscloud.webcontainer.registry.App;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
@Service
public class FilesServiceImpl implements FilesService {
@Autowired
private FileSystemService fileSystemService;
@Autowired
private AppLocationTracker locationTracker;
private Path resolve(App app, Path path) {
return this.locationTracker.resolve(app.getId(), path);
}
@Override
public void createAppDirectory(App app) {
try {
this.fileSystemService.createDirectory(resolve(app, Path.of("")));
}
catch(FileSystemServiceException e) {
// Ignore, may happen if the dir already exists
}
}
@Override
public void createDirectory(App app, Path path) {
this.fileSystemService.createDirectory(resolve(app, path));
}
@Override
public void createFile(App app, Path path, byte[] content) {
this.fileSystemService.createFile(resolve(app, path), content);
}
@Override
public void delete(App app, Path path) {
this.fileSystemService.delete(resolve(app, path));
}
@Override
public void get(App app, Path path) {
this.fileSystemService.get(resolve(app, path));
}
@Override
public List<ContentContainer> list(App app, Optional<Path> path) {
final Path appPath = resolve(app, Path.of(""));
final Path p = path.map(tmpPath -> resolve(app, tmpPath)).orElse(appPath);
return this.fileSystemService.list(p, FileSystemService.SortOrder.NATURAL, callbackPath -> appPath.relativize(callbackPath));
}
}

View File

@@ -3,6 +3,7 @@ package de.nbscloud.files;
import de.nbscloud.files.config.FilesConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -10,7 +11,7 @@ import java.nio.file.Path;
import java.nio.file.Paths;
@Component
public class LocationTracker {
public class LocationTracker implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(LocationTracker.class);
@Autowired
@@ -25,7 +26,8 @@ public class LocationTracker {
this.currentLocation = this.baseDirPath.resolve("");
}
public void init() {
@Override
public void afterPropertiesSet() throws Exception {
this.baseDirPath = Paths.get(this.filesConfig.getBaseDir());
this.currentLocation = this.baseDirPath.resolve("");
@@ -35,7 +37,11 @@ public class LocationTracker {
public void reset() {
logger.debug("Reset location");
init();
try {
afterPropertiesSet();
} catch (Exception e) {
// Cannot happen
}
}
Path getCurrentLocation() {
@@ -50,6 +56,10 @@ public class LocationTracker {
return this.baseDirPath.relativize(other);
}
public Path getAppDir() {
return this.baseDirPath.resolve(this.filesConfig.getAppDir());
}
private Path getTrashBin() {
return this.baseDirPath.resolve(this.filesConfig.getTrashBinName());
}
@@ -72,24 +82,6 @@ public class LocationTracker {
this.currentLocation = this.baseDirPath.resolve(navigateTo);
}
public void validate(String path) {
// Absolut paths are allowed, however, they have to be under baseDir
if(path == null) {
throw new IllegalStateException("Null");
}
if(path.contains("..")) {
throw new IllegalStateException("Relative path");
}
final Path tmpPath = Path.of(path);
if(!tmpPath.normalize().startsWith(this.baseDirPath)) {
throw new IllegalStateException("Illegal path: " + path);
}
}
private void validate_internal(String navigateTo) {
if(navigateTo == null) {
throw new IllegalStateException("Null");

View File

@@ -14,6 +14,7 @@ public class FilesConfig {
private String trashBinName;
private int truncateFileNameChars;
private String sharesName;
private String appDir;
public String getBaseDir() {
return baseDir;
@@ -62,4 +63,12 @@ public class FilesConfig {
public void setSharesName(String sharesName) {
this.sharesName = sharesName;
}
public String getAppDir() {
return appDir;
}
public void setAppDir(String appDir) {
this.appDir = appDir;
}
}

View File

@@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import de.nbscloud.files.FileSystemService;
import de.nbscloud.files.LocationTracker;
import de.nbscloud.files.Share;
import de.nbscloud.files.api.FilesService.ContentContainer;
import de.nbscloud.files.config.FilesConfig;
import de.nbscloud.files.exception.FileSystemServiceException;
import de.nbscloud.files.form.RenameForm;
@@ -338,12 +339,12 @@ public class FilesController implements InitializingBean {
return retList;
}
private List<FileSystemService.ContentContainer> getContent(FileSystemService.SortOrder order) {
final List<FileSystemService.ContentContainer> contentList = this.fileSystemService.list(order);
private List<ContentContainer> getContent(FileSystemService.SortOrder order) {
final List<ContentContainer> contentList = this.fileSystemService.list(order);
if (!this.locationTracker.isBasePath()) {
contentList.add(0,
new FileSystemService.ContentContainer(true,
new ContentContainer(true,
this.locationTracker.getParent(),
"..",
0L,
@@ -382,8 +383,6 @@ public class FilesController implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
this.locationTracker.init();
if (this.filesConfig.isUseTrashBin()) {
try {
this.fileSystemService.createDirectory(this.filesConfig.getTrashBinName());

View File

@@ -25,4 +25,7 @@ nbs-cloud.files.trashBinName=nbs.internal/nbs.trashbin
nbs-cloud.files.truncateFileNameChars=130
# Knob to configure the name of the shares directory
nbs-cloud.files.sharesName=nbs.internal/nbs.shares
nbs-cloud.files.sharesName=nbs.internal/nbs.shares
# Knob to configure the name of the app directory
nbs-cloud.files.appDir=nbs.internal/apps