Settings and checks for maximum artifact size #2

Manually merged
armin merged 1 commit from feature/max-artifact-size into master 2020-07-26 08:36:44 +00:00
13 changed files with 69 additions and 12 deletions

View file

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

View file

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

View file

@ -70,6 +70,12 @@
"type": "java.util.Long", "type": "java.util.Long",
"description": "Time until JWT tokens expire", "description": "Time until JWT tokens expire",
"sourceType": "net.friedl.fling.security.FlingWebSecurityConfiguration" "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: servlet:
multipart.max-file-size: -1 multipart.max-file-size: -1
multipart.max-request-size: -1 multipart.max-request-size: -1
logging.level: logging.level:
root: WARN root: WARN
net.friedl: TRACE net.friedl: TRACE
@ -21,6 +21,7 @@ logging.level:
# spring.http.log-request-details: true # spring.http.log-request-details: true
fling: fling:
max-artifact-size: 209715200 # 200 MB
archive.filesystem.archive-path: /home/armin/Desktop/fling archive.filesystem.archive-path: /home/armin/Desktop/fling
security: security:
allowed-origins: allowed-origins:

View file

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

View file

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

View file

@ -1,2 +1,4 @@
REACT_APP_API=http://localhost:8080/ REACT_APP_API=http://localhost:8080/
REACT_APP_LOGLEVEL=trace 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", "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
"integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" "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": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",

View file

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

View file

@ -100,7 +100,9 @@ export default function FlingArtifacts() {
.then(result => { .then(result => {
log.debug(`Got ${result.length} artifacts`); log.debug(`Got ${result.length} artifacts`);
for (let artifact of result) { 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); setArtifacts(artifacts);

View file

@ -1,4 +1,5 @@
import log from 'loglevel'; import log from 'loglevel';
import VanillaToasts from 'vanillatoasts';
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
@ -81,9 +82,21 @@ export default function Upload() {
stopEvent(ev); stopEvent(ev);
ev.persist(); 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"); console.warn("Dropzone triggered without files");
return; return;
} }
@ -198,6 +211,7 @@ export default function Upload() {
</div> </div>
<div className="upload-command-line m-2"> <div className="upload-command-line m-2">
<span className="total-upload">Total Size: {totalSize()}</span> <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> <button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
</div> </div>
</div> </div>

View file

@ -1,4 +1,5 @@
import log from 'loglevel'; import log from 'loglevel';
import VanillaToasts from 'vanillatoasts';
import React, { useState, useEffect, useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { Switch, Route, useLocation, Link } from "react-router-dom"; import { Switch, Route, useLocation, Link } from "react-router-dom";
@ -17,7 +18,10 @@ function Artifacts(props) {
let flingClient = new FlingClient(); let flingClient = new FlingClient();
flingClient.getArtifacts(props.fling.id) flingClient.getArtifacts(props.fling.id)
.then(artifacts => setArtifacts(artifacts)); .then(artifacts => {
artifacts = artifacts.filter(a => a.archived)
setArtifacts(artifacts)
});
}, [props.fling]); }, [props.fling]);
function renderArtifact(artifact) { function renderArtifact(artifact) {
@ -119,9 +123,21 @@ function Upload(props) {
stopEvent(ev); stopEvent(ev);
ev.persist(); 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"); console.warn("Dropzone triggered without files");
return; return;
} }
@ -236,6 +252,7 @@ function Upload(props) {
</div> </div>
<div className="upload-command-line m-2"> <div className="upload-command-line m-2">
<span className="total-upload">Total Size: {totalSize()}</span> <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> <button className="btn btn-primary btn-upload" onClick={handleUpload}>Upload</button>
</div> </div>
</div> </div>
@ -303,10 +320,10 @@ export default function FlingUserList(props) {
<li className={`tab-item ${location.pathname !== path("upload") ? "active" : ""}`}> <li className={`tab-item ${location.pathname !== path("upload") ? "active" : ""}`}>
<Link to={path("files")}>Files</Link> <Link to={path("files")}>Files</Link>
</li> </li>
{ props.fling.allowUpload {props.fling.allowUpload
? <li className={`tab-item ${location.pathname === path("upload") ? "active" : ""}`}> ? <li className={`tab-item ${location.pathname === path("upload") ? "active" : ""}`}>
<Link to={path("upload")}>Upload</Link> <Link to={path("upload")}>Upload</Link>
</li> </li>
: <></> : <></>
} }

View file

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