Settings and checks for maximum artifact size
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is passing

Different archive backends may not cope well with large file sizes.
Additionally, Fling admins may want to restrict the maximum file size to
protect their server.
This commit is contained in:
Armin Friedl 2020-07-26 10:34:05 +02:00
parent d4b7f1db30
commit e424c9ef09
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
13 changed files with 69 additions and 12 deletions

View file

@ -16,7 +16,6 @@ import net.friedl.fling.model.json.PathSerializer;
@Configuration
public class FlingConfiguration {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();

View file

@ -4,6 +4,7 @@ import java.io.IOException;
import java.util.UUID;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.InputStreamResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
@ -32,6 +33,8 @@ import net.friedl.fling.service.archive.ArchiveService;
@SecurityRequirement(name = "bearer")
@Validated
public class ArtifactController {
@Value("${fling.max-artifact-size:-1}")
private Long maxArtifactSize;
private ArtifactService artifactService;
private ArchiveService archiveService;
@ -58,6 +61,10 @@ public class ArtifactController {
@PostMapping(path = "/{id}/data")
public void uploadArtifactData(@PathVariable UUID id, HttpServletRequest request)
throws IOException {
if(maxArtifactSize >= 0 && maxArtifactSize < request.getContentLengthLong()) {
throw new IOException("Maximum artifact size exceeded");
}
archiveService.storeArtifact(id, request.getInputStream());
}

View file

@ -70,6 +70,12 @@
"type": "java.util.Long",
"description": "Time until JWT tokens expire",
"sourceType": "net.friedl.fling.security.FlingWebSecurityConfiguration"
},
{
"name": "fling.max-artifact-size",
"type": "java.util.Long",
"description": "Maximum artifact size in bytes. -1 to disable.",
"sourceType": "net.friedl.fling.controller.ArtifactController"
}
]
}

View file

@ -11,7 +11,7 @@ spring:
servlet:
multipart.max-file-size: -1
multipart.max-request-size: -1
logging.level:
root: WARN
net.friedl: TRACE
@ -21,6 +21,7 @@ logging.level:
# spring.http.log-request-details: true
fling:
max-artifact-size: 209715200 # 200 MB
archive.filesystem.archive-path: /home/armin/Desktop/fling
security:
allowed-origins:

View file

@ -16,6 +16,7 @@ logging.level:
root: WARN
fling:
max-artifact-size: 209715200 # 200 MB
archive.filesystem.archive-path: "/var/fling/files"
security:
allowed-origins:

View file

@ -1,2 +1,3 @@
REACT_APP_API=https://fling.friedl.net
REACT_APP_LOGLEVEL=warn
REACT_APP_FILESIZE=209715200

View file

@ -1,2 +1,4 @@
REACT_APP_API=http://localhost:8080/
REACT_APP_LOGLEVEL=trace
REACT_APP_FILESIZE=209715200

View file

@ -13376,6 +13376,11 @@
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw=="
},
"vanillatoasts": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/vanillatoasts/-/vanillatoasts-1.4.0.tgz",
"integrity": "sha512-DVMPPWVVt/x1B2f4iVpCw3rYDceu1PGRV8ECc3OfUC8By6O14OdXWjKryfaz8UorBuNe8btDIsujp6YP6gDNGA=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View file

@ -22,7 +22,8 @@
"react-scripts": "3.4.1",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"spectre.css": "^0.5.8"
"spectre.css": "^0.5.8",
"vanillatoasts": "^1.4.0"
},
"scripts": {
"start": "react-scripts start",

View file

@ -100,7 +100,9 @@ export default function FlingArtifacts() {
.then(result => {
log.debug(`Got ${result.length} artifacts`);
for (let artifact of result) {
artifacts.push(<FlingArtifactRow key={artifact.id} artifact={artifact} reloadArtifactsFn={getArtifacts} />);
if(artifact.archived) {
artifacts.push(<FlingArtifactRow key={artifact.id} artifact={artifact} reloadArtifactsFn={getArtifacts} />);
}
}
setArtifacts(artifacts);

View file

@ -1,4 +1,5 @@
import log from 'loglevel';
import VanillaToasts from 'vanillatoasts';
import React, { useState, useEffect, useRef } from 'react';
import { useSelector } from "react-redux";
@ -81,9 +82,21 @@ export default function Upload() {
stopEvent(ev);
ev.persist();
let evFiles = ev.dataTransfer.files;
let maxSize = process.env.REACT_APP_FILESIZE;
let evFiles = fileListToArray(ev.dataTransfer.files);
if (!evFiles) {
for (let i = evFiles.length - 1; i >= 0; i--) {
if (maxSize && maxSize >= 0 && evFiles[i].size > maxSize) {
VanillaToasts.create({
title: "Maximum file size exceeded",
text: `${evFiles[i].name} exceeds the maximum file size of ${prettifyBytes(maxSize)}`,
type: "warning"
});
evFiles.splice(i, 1);
};
}
if (evFiles.lenght === 0) {
console.warn("Dropzone triggered without files");
return;
}
@ -198,6 +211,7 @@ export default function Upload() {
</div>
<div className="upload-command-line m-2">
<span className="total-upload">Total Size: {totalSize()}</span>
<span className="total-upload">{`Max: ${prettifyBytes(process.env.REACT_APP_FILESIZE)}`}</span>
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
</div>
</div>

View file

@ -1,4 +1,5 @@
import log from 'loglevel';
import VanillaToasts from 'vanillatoasts';
import React, { useState, useEffect, useRef } from 'react';
import { Switch, Route, useLocation, Link } from "react-router-dom";
@ -17,7 +18,10 @@ function Artifacts(props) {
let flingClient = new FlingClient();
flingClient.getArtifacts(props.fling.id)
.then(artifacts => setArtifacts(artifacts));
.then(artifacts => {
artifacts = artifacts.filter(a => a.archived)
setArtifacts(artifacts)
});
}, [props.fling]);
function renderArtifact(artifact) {
@ -119,9 +123,21 @@ function Upload(props) {
stopEvent(ev);
ev.persist();
let evFiles = ev.dataTransfer.files;
let maxSize = process.env.REACT_APP_FILESIZE;
let evFiles = fileListToArray(ev.dataTransfer.files);
if (!evFiles) {
for (let i = evFiles.length - 1; i >= 0; i--) {
if (maxSize && maxSize >= 0 && evFiles[i].size > maxSize) {
VanillaToasts.create({
title: "Maximum file size exceeded",
text: `${evFiles[i].name} exceeds the maximum file size of ${prettifyBytes(maxSize)}`,
type: "warning"
});
evFiles.splice(i, 1);
};
}
if (evFiles.length === 0) {
console.warn("Dropzone triggered without files");
return;
}
@ -236,6 +252,7 @@ function Upload(props) {
</div>
<div className="upload-command-line m-2">
<span className="total-upload">Total Size: {totalSize()}</span>
<span className="total-upload">{`Max: ${prettifyBytes(process.env.REACT_APP_FILESIZE)}`}</span>
<button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
</div>
</div>
@ -303,10 +320,10 @@ export default function FlingUserList(props) {
<li className={`tab-item ${location.pathname !== path("upload") ? "active" : ""}`}>
<Link to={path("files")}>Files</Link>
</li>
{ props.fling.allowUpload
{props.fling.allowUpload
? <li className={`tab-item ${location.pathname === path("upload") ? "active" : ""}`}>
<Link to={path("upload")}>Upload</Link>
</li>
<Link to={path("upload")}>Upload</Link>
</li>
: <></>
}

View file

@ -14,6 +14,7 @@ import { BrowserRouter } from "react-router-dom";
import App from './App';
import 'vanillatoasts/vanillatoasts.css';
import "./style/fling.scss";
import * as serviceWorker from './serviceWorker';