diff --git a/files/src/main/java/de/srsoftware/umbrella/files/FileDb.java b/files/src/main/java/de/srsoftware/umbrella/files/FileDb.java index 27eb65e..93754b1 100644 --- a/files/src/main/java/de/srsoftware/umbrella/files/FileDb.java +++ b/files/src/main/java/de/srsoftware/umbrella/files/FileDb.java @@ -1,4 +1,7 @@ package de.srsoftware.umbrella.files; +import de.srsoftware.umbrella.core.model.UmbrellaUser; + public interface FileDb { + public boolean isPermitted(UmbrellaUser user, String fileName); } diff --git a/files/src/main/java/de/srsoftware/umbrella/files/FileModule.java b/files/src/main/java/de/srsoftware/umbrella/files/FileModule.java index bc32e1f..8d65b57 100644 --- a/files/src/main/java/de/srsoftware/umbrella/files/FileModule.java +++ b/files/src/main/java/de/srsoftware/umbrella/files/FileModule.java @@ -8,20 +8,25 @@ import de.srsoftware.umbrella.core.BaseHandler; import de.srsoftware.umbrella.core.ModuleRegistry; import de.srsoftware.umbrella.core.api.FileService; import de.srsoftware.umbrella.core.exceptions.UmbrellaException; +import de.srsoftware.umbrella.core.model.Project; import de.srsoftware.umbrella.core.model.Token; import de.srsoftware.umbrella.core.model.UmbrellaUser; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; +import java.util.stream.Collectors; import static de.srsoftware.umbrella.core.ConnectionProvider.connect; import static de.srsoftware.umbrella.core.Constants.*; +import static de.srsoftware.umbrella.core.ModuleRegistry.projectService; import static de.srsoftware.umbrella.core.ModuleRegistry.userService; -import static de.srsoftware.umbrella.core.Paths.LIST; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException; -import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.unprocessable; +import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.*; import static de.srsoftware.umbrella.files.Constants.CONFIG_DATABASE; import static de.srsoftware.umbrella.files.Constants.CONFIG_FILESTORE; @@ -68,8 +73,50 @@ public class FileModule extends BaseHandler implements FileService { return false; } - private boolean getProjectFiles(Path path, HttpExchange ex, UmbrellaUser user) { - return false; + private boolean getProjectFiles(Path path, HttpExchange ex, UmbrellaUser user) throws IOException { + var prjId = path.pop(); + var projects = projectService(); + if (prjId == null){ + var projectList = projects.listUserProjects(user.id(),true); + var map = projectList.values().stream().collect(Collectors.toMap(prj -> "/project/"+prj.id(),Project::name)); + return sendContent(ex,Map.of("dirs",map)); + } + + long pid; + try { + pid = Long.parseLong(prjId); + } catch (NumberFormatException e) { + throw invalidFieldException(PROJECT_ID,"Long"); + } + var project = projects.loadMembers(projects.load(pid)); + var filename = "/project/"+pid; + if (!path.empty()) filename += "/"+path; + if (!project.hasMember(user) && !fileDb.isPermitted(user,filename)) throw forbidden("You are not allowed to access {0}",filename); + var file = new File(baseDir+filename); + if (!file.exists()) throw unprocessable("{0} does not exist!",filename); + if (file.isDirectory()){ + Map map = getDirectory(file); + map.put("title",filename.replace("/project/"+prjId,project.name())); + return sendContent(ex,map); + } + return getFile(ex, file); + } + + private boolean getFile(HttpExchange ex, File file) throws IOException { + return sendContent(ex,new FileInputStream(file)); + } + + private Map getDirectory(File file) throws IOException { + var children = file.listFiles(); + var map = new HashMap(); + if (children == null) return map; + var prefixLen = baseDir.toString().length(); + for (var child : children){ + var o = map.computeIfAbsent(child.isDirectory() ? "dirs" : "files", k -> new HashMap()); + //noinspection unchecked + ((Map) o).put(child.toString().substring(prefixLen),child.getName()); + } + return map; } private boolean getUserFiles(Path path, HttpExchange ex, UmbrellaUser user) { diff --git a/files/src/main/java/de/srsoftware/umbrella/files/SqliteDb.java b/files/src/main/java/de/srsoftware/umbrella/files/SqliteDb.java index 72e83df..e88469c 100644 --- a/files/src/main/java/de/srsoftware/umbrella/files/SqliteDb.java +++ b/files/src/main/java/de/srsoftware/umbrella/files/SqliteDb.java @@ -1,10 +1,16 @@ package de.srsoftware.umbrella.files; +import de.srsoftware.tools.jdbc.Condition; +import de.srsoftware.tools.jdbc.Query; import de.srsoftware.umbrella.core.BaseDb; +import de.srsoftware.umbrella.core.model.UmbrellaUser; import java.sql.Connection; import java.sql.SQLException; +import static de.srsoftware.tools.jdbc.Condition.equal; +import static de.srsoftware.tools.jdbc.Query.SelectQuery.ALL; +import static de.srsoftware.tools.jdbc.Query.select; import static de.srsoftware.umbrella.core.Constants.USER_ID; import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.databaseException; import static de.srsoftware.umbrella.files.Constants.FILE; @@ -36,4 +42,16 @@ public class SqliteDb extends BaseDb implements FileDb { throw databaseException(e.getMessage()).causedBy(e); } } + + @Override + public boolean isPermitted(UmbrellaUser user, String fileName) { + try { + var rs = select(ALL).from(TABLE_FILE_SHARES).where(USER_ID, equal(user.id())).where(FILE, equal(fileName)).exec(db); + var result = rs.next(); + rs.close(); + return result; + } catch (SQLException e) { + throw databaseException("Failed to check file permissions"); + } + } } diff --git a/frontend/src/routes/files/Index.svelte b/frontend/src/routes/files/Index.svelte index be2d910..dc96f7b 100644 --- a/frontend/src/routes/files/Index.svelte +++ b/frontend/src/routes/files/Index.svelte @@ -2,25 +2,32 @@ import { onMount } from 'svelte'; import { useTinyRouter } from 'svelte-tiny-router'; import { api } from '../../urls.svelte'; - import { error } from '../../warn.svelte'; + import { error, yikes } from '../../warn.svelte'; import { t } from '../../translations.svelte'; import { user } from '../../user.svelte'; const router = useTinyRouter(); let children = $state({}); + let path = $state(router.path) - async function loadChildren(path){ - path = path.substring(6); - if (path == '') path = '/'; - children = { dirs : {}}; - if (path == '/'){ - children.dirs[t('my_files')] = `user/${user.id}`; - children.dirs[t('projects')] = `projects`; - children.dirs[t('companies')] = `company`; + async function loadChildren(p){ + p = p.substring(6); + if (p == '') p = '/'; + children = { dirs : {}, files : {}, title : p}; + console.log(p); + if (p == '/'){ + children.dirs[`/user/${user.id}`] = t('my_files'); + children.dirs['/project'] = t('projects') + children.dirs['/company'] = t('companies'); } else { - const url = api(`files${path}`); + const url = api(`files${p}`); const res = await fetch(url,{credentials:'include'}); if (res.ok){ + let json = await res.json(); + if (json.dirs) children.dirs = json.dirs; + if (json.files) children.files = json.files; + if (json.title) children.title = json.title; + yikes(); } else { error(res); } @@ -45,14 +52,24 @@ onMount(() => loadChildren(window.location.pathname)); -

{t('files')} – {router.path}

+

{t('files')} – {children?.title}

-{#if children?.dirs}
    +{#if children?.dirs} {#each Object.entries(children.dirs) as [k,v]} -
  • - {k} +
  • + + {v} +
  • +{/each} + {/if} +{#if children.files} +{#each Object.entries(children.files) as [k,v]} +
  • + + {v}
  • {/each} -
{/if} + +