mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 22:07:56 +01:00
Encrypt stored files in SHA 256
This commit is contained in:
parent
906de329ae
commit
db7a9f0e4a
@ -1,16 +0,0 @@
|
|||||||
package com.sismics.docs.core.event;
|
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract file content event.
|
|
||||||
*
|
|
||||||
* @author bgamard
|
|
||||||
*/
|
|
||||||
public class ExtractFileAsyncEvent {
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Objects.toStringHelper(this)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,7 @@
|
|||||||
package com.sismics.docs.core.event;
|
package com.sismics.docs.core.event;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.sismics.docs.core.model.jpa.Document;
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
@ -20,6 +22,11 @@ public class FileCreatedAsyncEvent {
|
|||||||
*/
|
*/
|
||||||
private Document document;
|
private Document document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unencrypted input stream containing the file.
|
||||||
|
*/
|
||||||
|
private InputStream inputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getter of file.
|
* Getter of file.
|
||||||
*
|
*
|
||||||
@ -56,6 +63,24 @@ public class FileCreatedAsyncEvent {
|
|||||||
this.document = document;
|
this.document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter of inputStream.
|
||||||
|
*
|
||||||
|
* @return the inputStream
|
||||||
|
*/
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setter de inputStream.
|
||||||
|
*
|
||||||
|
* @param inputStream inputStream
|
||||||
|
*/
|
||||||
|
public void setInputStream(InputStream inputStream) {
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return Objects.toStringHelper(this)
|
return Objects.toStringHelper(this)
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
package com.sismics.docs.core.listener.async;
|
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
|
||||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
|
||||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
|
||||||
import com.sismics.docs.core.event.ExtractFileAsyncEvent;
|
|
||||||
import com.sismics.docs.core.model.jpa.Document;
|
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
|
||||||
import com.sismics.docs.core.util.FileUtil;
|
|
||||||
import com.sismics.docs.core.util.TransactionUtil;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener on extract content from all files.
|
|
||||||
*
|
|
||||||
* @author bgamard
|
|
||||||
*/
|
|
||||||
public class ExtractFileAsyncListener {
|
|
||||||
/**
|
|
||||||
* Logger.
|
|
||||||
*/
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ExtractFileAsyncListener.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract content from all files.
|
|
||||||
*
|
|
||||||
* @param extractFileAsyncEvent Extract file content event
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
@Subscribe
|
|
||||||
public void on(final ExtractFileAsyncEvent extractFileAsyncEvent) throws Exception {
|
|
||||||
if (log.isInfoEnabled()) {
|
|
||||||
log.info("Extract file content event: " + extractFileAsyncEvent.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
TransactionUtil.handle(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
FileDao fileDao = new FileDao();
|
|
||||||
DocumentDao documentDao = new DocumentDao();
|
|
||||||
List<File> fileList = fileDao.findAll();
|
|
||||||
for (File file : fileList) {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
Document document = documentDao.getById(file.getDocumentId());
|
|
||||||
file.setContent(FileUtil.extractContent(document, file));
|
|
||||||
TransactionUtil.commit();
|
|
||||||
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -39,7 +39,7 @@ public class FileCreatedAsyncListener {
|
|||||||
// OCR the file
|
// OCR the file
|
||||||
final File file = fileCreatedAsyncEvent.getFile();
|
final File file = fileCreatedAsyncEvent.getFile();
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getDocument(), file);
|
final String content = FileUtil.extractContent(fileCreatedAsyncEvent.getDocument(), file, fileCreatedAsyncEvent.getInputStream());
|
||||||
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
log.info(MessageFormat.format("File content extracted in {0}ms", System.currentTimeMillis() - startTime));
|
||||||
|
|
||||||
// Store the OCR-ization result in the database
|
// Store the OCR-ization result in the database
|
||||||
|
@ -16,7 +16,6 @@ import com.sismics.docs.core.listener.async.DocumentDeletedAsyncListener;
|
|||||||
import com.sismics.docs.core.listener.async.DocumentUpdatedAsyncListener;
|
import com.sismics.docs.core.listener.async.DocumentUpdatedAsyncListener;
|
||||||
import com.sismics.docs.core.listener.async.FileCreatedAsyncListener;
|
import com.sismics.docs.core.listener.async.FileCreatedAsyncListener;
|
||||||
import com.sismics.docs.core.listener.async.FileDeletedAsyncListener;
|
import com.sismics.docs.core.listener.async.FileDeletedAsyncListener;
|
||||||
import com.sismics.docs.core.listener.async.ExtractFileAsyncListener;
|
|
||||||
import com.sismics.docs.core.listener.async.RebuildIndexAsyncListener;
|
import com.sismics.docs.core.listener.async.RebuildIndexAsyncListener;
|
||||||
import com.sismics.docs.core.listener.sync.DeadEventListener;
|
import com.sismics.docs.core.listener.sync.DeadEventListener;
|
||||||
import com.sismics.docs.core.model.jpa.Config;
|
import com.sismics.docs.core.model.jpa.Config;
|
||||||
@ -82,7 +81,6 @@ public class AppContext {
|
|||||||
asyncEventBus.register(new DocumentUpdatedAsyncListener());
|
asyncEventBus.register(new DocumentUpdatedAsyncListener());
|
||||||
asyncEventBus.register(new DocumentDeletedAsyncListener());
|
asyncEventBus.register(new DocumentDeletedAsyncListener());
|
||||||
asyncEventBus.register(new RebuildIndexAsyncListener());
|
asyncEventBus.register(new RebuildIndexAsyncListener());
|
||||||
asyncEventBus.register(new ExtractFileAsyncListener());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +28,11 @@ public class EncryptionUtil {
|
|||||||
*/
|
*/
|
||||||
private static final String SALT = "LEpxZmm2SMu2PeKzPNrar2rhVAS6LrrgvXKeL9uyXC4vgKHg";
|
private static final String SALT = "LEpxZmm2SMu2PeKzPNrar2rhVAS6LrrgvXKeL9uyXC4vgKHg";
|
||||||
|
|
||||||
|
static {
|
||||||
|
// Initialize Bouncy Castle provider
|
||||||
|
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a private key.
|
* Generate a private key.
|
||||||
*
|
*
|
||||||
@ -39,22 +44,6 @@ public class EncryptionUtil {
|
|||||||
return new BigInteger(176, random).toString(32);
|
return new BigInteger(176, random).toString(32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt an InputStream using the specified private key.
|
|
||||||
*
|
|
||||||
* @param is InputStream to encrypt
|
|
||||||
* @param privateKey Private key
|
|
||||||
* @return Encrypted stream
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static InputStream encryptStream(InputStream is, String privateKey) throws Exception {
|
|
||||||
checkBouncyCastleProvider();
|
|
||||||
if (Strings.isNullOrEmpty(privateKey)) {
|
|
||||||
throw new IllegalArgumentException("The private key is null or empty");
|
|
||||||
}
|
|
||||||
return new CipherInputStream(is, getCipher(privateKey, Cipher.ENCRYPT_MODE));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decrypt an InputStream using the specified private key.
|
* Decrypt an InputStream using the specified private key.
|
||||||
*
|
*
|
||||||
@ -63,11 +52,24 @@ public class EncryptionUtil {
|
|||||||
* @return Encrypted stream
|
* @return Encrypted stream
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static InputStream decryptStream(InputStream is, String privateKey) throws Exception {
|
public static InputStream decryptInputStream(InputStream is, String privateKey) throws Exception {
|
||||||
checkBouncyCastleProvider();
|
|
||||||
return new CipherInputStream(is, getCipher(privateKey, Cipher.DECRYPT_MODE));
|
return new CipherInputStream(is, getCipher(privateKey, Cipher.DECRYPT_MODE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an encryption cipher.
|
||||||
|
*
|
||||||
|
* @param privateKey Private key
|
||||||
|
* @return Encryption cipher
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public static Cipher getEncryptionCipher(String privateKey) throws Exception {
|
||||||
|
if (Strings.isNullOrEmpty(privateKey)) {
|
||||||
|
throw new IllegalArgumentException("The private key is null or empty");
|
||||||
|
}
|
||||||
|
return getCipher(privateKey, Cipher.ENCRYPT_MODE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a Cipher.
|
* Initialize a Cipher.
|
||||||
*
|
*
|
||||||
@ -84,13 +86,4 @@ public class EncryptionUtil {
|
|||||||
cipher.init(mode, desKey);
|
cipher.init(mode, desKey);
|
||||||
return cipher;
|
return cipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the Bouncy Castle provider if necessary.
|
|
||||||
*/
|
|
||||||
private static void checkBouncyCastleProvider() {
|
|
||||||
if (Security.getProvider("BouncyCastleProvider") == null) {
|
|
||||||
Security.insertProviderAt(new BouncyCastleProvider(), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,18 @@
|
|||||||
package com.sismics.docs.core.util;
|
package com.sismics.docs.core.util;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
import javax.crypto.CipherOutputStream;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import net.sourceforge.tess4j.Tesseract;
|
import net.sourceforge.tess4j.Tesseract;
|
||||||
@ -42,36 +47,36 @@ public class FileUtil {
|
|||||||
*
|
*
|
||||||
* @param document Document linked to the file
|
* @param document Document linked to the file
|
||||||
* @param file File to extract
|
* @param file File to extract
|
||||||
|
* @param inputStream Unencrypted input stream
|
||||||
* @return Content extract
|
* @return Content extract
|
||||||
*/
|
*/
|
||||||
public static String extractContent(Document document, File file) {
|
public static String extractContent(Document document, File file, InputStream inputStream) {
|
||||||
String content = null;
|
String content = null;
|
||||||
|
|
||||||
if (ImageUtil.isImage(file.getMimeType())) {
|
if (ImageUtil.isImage(file.getMimeType())) {
|
||||||
content = ocrFile(document, file);
|
content = ocrFile(inputStream, document);
|
||||||
} else if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
} else if (file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
||||||
content = extractPdf(file);
|
content = extractPdf(inputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optical character recognition on a file.
|
* Optical character recognition on a stream.
|
||||||
*
|
*
|
||||||
|
* @param inputStream Unencrypted input stream
|
||||||
* @param document Document linked to the file
|
* @param document Document linked to the file
|
||||||
* @param file File to OCR
|
|
||||||
* @return Content extracted
|
* @return Content extracted
|
||||||
*/
|
*/
|
||||||
private static String ocrFile(Document document, File file) {
|
private static String ocrFile(InputStream inputStream, Document document) {
|
||||||
Tesseract instance = Tesseract.getInstance();
|
Tesseract instance = Tesseract.getInstance();
|
||||||
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
|
|
||||||
String content = null;
|
String content = null;
|
||||||
BufferedImage image = null;
|
BufferedImage image = null;
|
||||||
try {
|
try {
|
||||||
image = ImageIO.read(storedfile);
|
image = ImageIO.read(inputStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error reading the image " + storedfile, e);
|
log.error("Error reading the image", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upscale and grayscale the image
|
// Upscale and grayscale the image
|
||||||
@ -85,7 +90,7 @@ public class FileUtil {
|
|||||||
instance.setLanguage(document.getLanguage());
|
instance.setLanguage(document.getLanguage());
|
||||||
content = instance.doOCR(image);
|
content = instance.doOCR(image);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Error while OCR-izing the file " + storedfile, e);
|
log.error("Error while OCR-izing the image", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return content;
|
return content;
|
||||||
@ -94,19 +99,18 @@ public class FileUtil {
|
|||||||
/**
|
/**
|
||||||
* Extract text from a PDF.
|
* Extract text from a PDF.
|
||||||
*
|
*
|
||||||
* @param file File to extract
|
* @param inputStream Unencrypted input stream
|
||||||
* @return Content extracted
|
* @return Content extracted
|
||||||
*/
|
*/
|
||||||
private static String extractPdf(File file) {
|
private static String extractPdf(InputStream inputStream) {
|
||||||
String content = null;
|
String content = null;
|
||||||
PDDocument pdfDocument = null;
|
PDDocument pdfDocument = null;
|
||||||
java.io.File storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
|
|
||||||
try {
|
try {
|
||||||
PDFTextStripper stripper = new PDFTextStripper();
|
PDFTextStripper stripper = new PDFTextStripper();
|
||||||
pdfDocument = PDDocument.load(storedfile.getAbsolutePath(), true);
|
pdfDocument = PDDocument.load(inputStream, true);
|
||||||
content = stripper.getText(pdfDocument);
|
content = stripper.getText(pdfDocument);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Error while extracting text from the PDF " + storedfile, e);
|
log.error("Error while extracting text from the PDF", e);
|
||||||
} finally {
|
} finally {
|
||||||
if (pdfDocument != null) {
|
if (pdfDocument != null) {
|
||||||
try {
|
try {
|
||||||
@ -123,41 +127,39 @@ public class FileUtil {
|
|||||||
/**
|
/**
|
||||||
* Save a file on the storage filesystem.
|
* Save a file on the storage filesystem.
|
||||||
*
|
*
|
||||||
* @param is InputStream
|
* @param inputStream Unencrypted input stream
|
||||||
* @param file File to save
|
* @param file File to save
|
||||||
* @throws IOException
|
* @param privateKey Private key used for encryption
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static void save(InputStream is, File file) throws IOException {
|
public static void save(InputStream inputStream, File file, String privateKey) throws Exception {
|
||||||
// TODO Encrypt file and variations
|
Cipher cipher = EncryptionUtil.getEncryptionCipher(privateKey);
|
||||||
|
|
||||||
Path path = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId());
|
Path path = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId());
|
||||||
Files.copy(is, path);
|
Files.copy(new CipherInputStream(inputStream, cipher), path);
|
||||||
|
|
||||||
// Generate file variations
|
// Generate file variations
|
||||||
try {
|
inputStream.reset();
|
||||||
saveVariations(file, path.toFile());
|
saveVariations(file, inputStream, cipher);
|
||||||
} catch (IOException e) {
|
inputStream.reset();
|
||||||
// Don't rethrow Exception from file variations generation
|
|
||||||
log.error("Error creating file variations", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate file variations.
|
* Generate file variations.
|
||||||
*
|
*
|
||||||
* @param file File from database
|
* @param file File from database
|
||||||
* @param originalFile Original file
|
* @param inputStream Unencrypted input stream
|
||||||
* @throws IOException
|
* @param cipher Cipher to use for encryption
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public static void saveVariations(File file, java.io.File originalFile) throws IOException {
|
public static void saveVariations(File file, InputStream inputStream, Cipher cipher) throws Exception {
|
||||||
BufferedImage image = null;
|
BufferedImage image = null;
|
||||||
if (ImageUtil.isImage(file.getMimeType())) {
|
if (ImageUtil.isImage(file.getMimeType())) {
|
||||||
image = ImageIO.read(originalFile);
|
image = ImageIO.read(inputStream);
|
||||||
} else if(file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
} else if(file.getMimeType().equals(MimeType.APPLICATION_PDF)) {
|
||||||
// Generate preview from the first page of the PDF
|
// Generate preview from the first page of the PDF
|
||||||
PDDocument pdfDocument = null;
|
PDDocument pdfDocument = null;
|
||||||
try {
|
try {
|
||||||
pdfDocument = PDDocument.load(originalFile.getAbsolutePath(), true);
|
pdfDocument = PDDocument.load(inputStream, true);
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
List<PDPage> pageList = pdfDocument.getDocumentCatalog().getAllPages();
|
List<PDPage> pageList = pdfDocument.getDocumentCatalog().getAllPages();
|
||||||
if (pageList.size() > 0) {
|
if (pageList.size() > 0) {
|
||||||
@ -174,8 +176,24 @@ public class FileUtil {
|
|||||||
BufferedImage web = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 1280, Scalr.OP_ANTIALIAS);
|
BufferedImage web = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 1280, Scalr.OP_ANTIALIAS);
|
||||||
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 256, Scalr.OP_ANTIALIAS);
|
BufferedImage thumbnail = Scalr.resize(image, Scalr.Method.AUTOMATIC, Scalr.Mode.AUTOMATIC, 256, Scalr.OP_ANTIALIAS);
|
||||||
image.flush();
|
image.flush();
|
||||||
ImageUtil.writeJpeg(web, Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile());
|
|
||||||
ImageUtil.writeJpeg(thumbnail, Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile());
|
// Write "web" encrypted image
|
||||||
|
java.io.File outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_web").toFile();
|
||||||
|
OutputStream outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher);
|
||||||
|
try {
|
||||||
|
ImageUtil.writeJpeg(web, outputStream);
|
||||||
|
} finally {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write "thumb" encrypted image
|
||||||
|
outputFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId() + "_thumb").toFile();
|
||||||
|
outputStream = new CipherOutputStream(new FileOutputStream(outputFile), cipher);
|
||||||
|
try {
|
||||||
|
ImageUtil.writeJpeg(thumbnail, outputStream);
|
||||||
|
} finally {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package com.sismics.util;
|
package com.sismics.util;
|
||||||
|
|
||||||
import com.sismics.util.mime.MimeType;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
import javax.imageio.IIOImage;
|
import javax.imageio.IIOImage;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.imageio.ImageWriteParam;
|
import javax.imageio.ImageWriteParam;
|
||||||
import javax.imageio.ImageWriter;
|
import javax.imageio.ImageWriter;
|
||||||
import javax.imageio.stream.FileImageOutputStream;
|
import javax.imageio.stream.ImageOutputStream;
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
import java.io.File;
|
import com.sismics.util.mime.MimeType;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image processing utilities.
|
* Image processing utilities.
|
||||||
@ -23,26 +24,26 @@ public class ImageUtil {
|
|||||||
* Write a high quality JPEG.
|
* Write a high quality JPEG.
|
||||||
*
|
*
|
||||||
* @param image
|
* @param image
|
||||||
* @param file
|
* @param outputStream Output stream
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public static void writeJpeg(BufferedImage image, File file) throws IOException {
|
public static void writeJpeg(BufferedImage image, OutputStream outputStream) throws IOException {
|
||||||
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg");
|
Iterator<ImageWriter> iter = ImageIO.getImageWritersByFormatName("jpeg");
|
||||||
ImageWriter writer = null;
|
ImageWriter writer = null;
|
||||||
FileImageOutputStream output = null;
|
ImageOutputStream imageOutputStream = null;
|
||||||
try {
|
try {
|
||||||
writer = (ImageWriter) iter.next();
|
writer = (ImageWriter) iter.next();
|
||||||
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
ImageWriteParam iwp = writer.getDefaultWriteParam();
|
||||||
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
||||||
iwp.setCompressionQuality(1.f);
|
iwp.setCompressionQuality(1.f);
|
||||||
output = new FileImageOutputStream(file);
|
imageOutputStream = ImageIO.createImageOutputStream(outputStream);
|
||||||
writer.setOutput(output);
|
writer.setOutput(imageOutputStream);
|
||||||
IIOImage iioImage = new IIOImage(image, null, null);
|
IIOImage iioImage = new IIOImage(image, null, null);
|
||||||
writer.write(null, iioImage, iwp);
|
writer.write(null, iioImage, iwp);
|
||||||
} finally {
|
} finally {
|
||||||
if (output != null) {
|
if (imageOutputStream != null) {
|
||||||
try {
|
try {
|
||||||
output.close();
|
imageOutputStream.close();
|
||||||
} catch (Exception inner) {
|
} catch (Exception inner) {
|
||||||
// NOP
|
// NOP
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.sismics.util.mime;
|
package com.sismics.util.mime;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to check MIME types.
|
* Utility to check MIME types.
|
||||||
@ -24,6 +25,18 @@ public class MimeTypeUtil {
|
|||||||
if (readCount <= 0) {
|
if (readCount <= 0) {
|
||||||
throw new Exception("Cannot read input file");
|
throw new Exception("Cannot read input file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return guessMimeType(headerBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to guess the MIME type of a file by its magic number (header).
|
||||||
|
*
|
||||||
|
* @param headerBytes File header (first bytes)
|
||||||
|
* @return MIME type
|
||||||
|
* @throws UnsupportedEncodingException
|
||||||
|
*/
|
||||||
|
public static String guessMimeType(byte[] headerBytes) throws UnsupportedEncodingException {
|
||||||
String header = new String(headerBytes, "US-ASCII");
|
String header = new String(headerBytes, "US-ASCII");
|
||||||
|
|
||||||
if (header.startsWith("PK")) {
|
if (header.startsWith("PK")) {
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
alter table T_USER add column USE_PRIVATEKEY_C varchar(100) default '' not null;
|
alter table T_USER add column USE_PRIVATEKEY_C varchar(100) default '' not null;
|
||||||
|
update T_USER set USE_PRIVATEKEY_C = 'AdminPk' where USE_ID_C = 'admin';
|
||||||
update T_CONFIG set CFG_VALUE_C='6' where CFG_ID_C='DB_VERSION';
|
update T_CONFIG set CFG_VALUE_C='6' where CFG_ID_C='DB_VERSION';
|
||||||
|
@ -2,9 +2,11 @@ package com.sismics.docs.core.util;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.CipherInputStream;
|
||||||
|
|
||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
@ -32,14 +34,15 @@ public class TestEncryptUtil {
|
|||||||
@Test
|
@Test
|
||||||
public void encryptStreamTest() throws Exception {
|
public void encryptStreamTest() throws Exception {
|
||||||
try {
|
try {
|
||||||
EncryptionUtil.encryptStream(this.getClass().getResourceAsStream("/file/udhr.pdf"), "");
|
EncryptionUtil.getEncryptionCipher("");
|
||||||
Assert.fail();
|
Assert.fail();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// NOP
|
// NOP
|
||||||
}
|
}
|
||||||
InputStream inputStream = EncryptionUtil.encryptStream(this.getClass().getResourceAsStream("/file/udhr.pdf"), pk);
|
Cipher cipher = EncryptionUtil.getEncryptionCipher(pk);
|
||||||
byte[] encryptedData = Streams.readAll(inputStream);
|
InputStream inputStream = new CipherInputStream(this.getClass().getResourceAsStream("/file/udhr.pdf"), cipher);
|
||||||
byte[] assertData = Streams.readAll(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"));
|
byte[] encryptedData = ByteStreams.toByteArray(inputStream);
|
||||||
|
byte[] assertData = ByteStreams.toByteArray(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"));
|
||||||
Assert.assertTrue(ByteStreams.equal(
|
Assert.assertTrue(ByteStreams.equal(
|
||||||
ByteStreams.newInputStreamSupplier(encryptedData),
|
ByteStreams.newInputStreamSupplier(encryptedData),
|
||||||
ByteStreams.newInputStreamSupplier(assertData)));
|
ByteStreams.newInputStreamSupplier(assertData)));
|
||||||
@ -47,9 +50,9 @@ public class TestEncryptUtil {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void decryptStreamTest() throws Exception {
|
public void decryptStreamTest() throws Exception {
|
||||||
InputStream inputStream = EncryptionUtil.decryptStream(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"), pk);
|
InputStream inputStream = EncryptionUtil.decryptInputStream(this.getClass().getResourceAsStream("/file/udhr_encrypted.pdf"), pk);
|
||||||
byte[] encryptedData = Streams.readAll(inputStream);
|
byte[] encryptedData = ByteStreams.toByteArray(inputStream);
|
||||||
byte[] assertData = Streams.readAll(this.getClass().getResourceAsStream("/file/udhr.pdf"));
|
byte[] assertData = ByteStreams.toByteArray(this.getClass().getResourceAsStream("/file/udhr.pdf"));
|
||||||
Assert.assertTrue(ByteStreams.equal(
|
Assert.assertTrue(ByteStreams.equal(
|
||||||
ByteStreams.newInputStreamSupplier(encryptedData),
|
ByteStreams.newInputStreamSupplier(encryptedData),
|
||||||
ByteStreams.newInputStreamSupplier(assertData)));
|
ByteStreams.newInputStreamSupplier(assertData)));
|
||||||
|
@ -1 +0,0 @@
|
|||||||
- Encrypt files stored on FS (server)
|
|
@ -3,7 +3,7 @@
|
|||||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
version="3.0">
|
version="3.0" metadata-complete="true">
|
||||||
|
|
||||||
<!-- Override init parameter to avoid nasty file locking issue on windows. -->
|
<!-- Override init parameter to avoid nasty file locking issue on windows. -->
|
||||||
<servlet>
|
<servlet>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package com.sismics.docs.rest.resource;
|
package com.sismics.docs.rest.resource;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -26,12 +24,10 @@ import com.sismics.docs.core.dao.jpa.DocumentDao;
|
|||||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
|
import com.sismics.docs.core.dao.jpa.criteria.DocumentCriteria;
|
||||||
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
import com.sismics.docs.core.dao.jpa.dto.DocumentDto;
|
||||||
import com.sismics.docs.core.event.ExtractFileAsyncEvent;
|
|
||||||
import com.sismics.docs.core.model.context.AppContext;
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
import com.sismics.docs.core.util.ConfigUtil;
|
import com.sismics.docs.core.util.ConfigUtil;
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
import com.sismics.docs.core.util.FileUtil;
|
|
||||||
import com.sismics.docs.core.util.jpa.PaginatedList;
|
import com.sismics.docs.core.util.jpa.PaginatedList;
|
||||||
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
import com.sismics.docs.core.util.jpa.PaginatedLists;
|
||||||
import com.sismics.docs.core.util.jpa.SortCriteria;
|
import com.sismics.docs.core.util.jpa.SortCriteria;
|
||||||
@ -147,29 +143,6 @@ public class AppResource extends BaseResource {
|
|||||||
return Response.ok().entity(response).build();
|
return Response.ok().entity(response).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract content from all files again.
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
@POST
|
|
||||||
@Path("batch/extract")
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public Response batchExtract() throws JSONException {
|
|
||||||
if (!authenticate()) {
|
|
||||||
throw new ForbiddenClientException();
|
|
||||||
}
|
|
||||||
checkBaseFunction(BaseFunction.ADMIN);
|
|
||||||
|
|
||||||
// Raise an extract file content event
|
|
||||||
AppContext.getInstance().getAsyncEventBus().post(new ExtractFileAsyncEvent());
|
|
||||||
|
|
||||||
JSONObject response = new JSONObject();
|
|
||||||
response.put("status", "ok");
|
|
||||||
return Response.ok().entity(response).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy and rebuild Lucene index.
|
* Destroy and rebuild Lucene index.
|
||||||
*
|
*
|
||||||
@ -197,7 +170,7 @@ public class AppResource extends BaseResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy and rebuild Lucene index.
|
* Clean storage.
|
||||||
*
|
*
|
||||||
* @return Response
|
* @return Response
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
@ -233,38 +206,4 @@ public class AppResource extends BaseResource {
|
|||||||
response.put("status", "ok");
|
response.put("status", "ok");
|
||||||
return Response.ok().entity(response).build();
|
return Response.ok().entity(response).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Regenerate file variations.
|
|
||||||
*
|
|
||||||
* @return Response
|
|
||||||
* @throws JSONException
|
|
||||||
*/
|
|
||||||
@POST
|
|
||||||
@Path("batch/file_variations")
|
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
|
||||||
public Response batchFileVariations() throws JSONException {
|
|
||||||
if (!authenticate()) {
|
|
||||||
throw new ForbiddenClientException();
|
|
||||||
}
|
|
||||||
checkBaseFunction(BaseFunction.ADMIN);
|
|
||||||
|
|
||||||
// Get all files
|
|
||||||
FileDao fileDao = new FileDao();
|
|
||||||
List<File> fileList = fileDao.findAll();
|
|
||||||
|
|
||||||
// Generate variations for each file
|
|
||||||
for (File file : fileList) {
|
|
||||||
java.io.File originalFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file.getId()).toFile();
|
|
||||||
try {
|
|
||||||
FileUtil.saveVariations(file, originalFile);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new ServerException("FileError", "Error generating file variations", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject response = new JSONObject();
|
|
||||||
response.put("status", "ok");
|
|
||||||
return Response.ok().entity(response).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package com.sismics.docs.rest.resource;
|
package com.sismics.docs.rest.resource;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@ -20,22 +23,28 @@ import javax.ws.rs.Path;
|
|||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.StreamingOutput;
|
||||||
|
|
||||||
import org.codehaus.jettison.json.JSONException;
|
import org.codehaus.jettison.json.JSONException;
|
||||||
import org.codehaus.jettison.json.JSONObject;
|
import org.codehaus.jettison.json.JSONObject;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import com.google.common.io.ByteStreams;
|
||||||
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
import com.sismics.docs.core.dao.jpa.DocumentDao;
|
||||||
import com.sismics.docs.core.dao.jpa.FileDao;
|
import com.sismics.docs.core.dao.jpa.FileDao;
|
||||||
import com.sismics.docs.core.dao.jpa.ShareDao;
|
import com.sismics.docs.core.dao.jpa.ShareDao;
|
||||||
|
import com.sismics.docs.core.dao.jpa.UserDao;
|
||||||
import com.sismics.docs.core.event.FileCreatedAsyncEvent;
|
import com.sismics.docs.core.event.FileCreatedAsyncEvent;
|
||||||
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
import com.sismics.docs.core.event.FileDeletedAsyncEvent;
|
||||||
import com.sismics.docs.core.model.context.AppContext;
|
import com.sismics.docs.core.model.context.AppContext;
|
||||||
import com.sismics.docs.core.model.jpa.Document;
|
import com.sismics.docs.core.model.jpa.Document;
|
||||||
import com.sismics.docs.core.model.jpa.File;
|
import com.sismics.docs.core.model.jpa.File;
|
||||||
|
import com.sismics.docs.core.model.jpa.User;
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
|
import com.sismics.docs.core.util.EncryptionUtil;
|
||||||
import com.sismics.docs.core.util.FileUtil;
|
import com.sismics.docs.core.util.FileUtil;
|
||||||
import com.sismics.rest.exception.ClientException;
|
import com.sismics.rest.exception.ClientException;
|
||||||
import com.sismics.rest.exception.ForbiddenClientException;
|
import com.sismics.rest.exception.ForbiddenClientException;
|
||||||
@ -77,20 +86,30 @@ public class FileResource extends BaseResource {
|
|||||||
|
|
||||||
// Get the document
|
// Get the document
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
FileDao fileDao = new FileDao();
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
Document document;
|
Document document;
|
||||||
|
User user;
|
||||||
try {
|
try {
|
||||||
document = documentDao.getDocument(documentId, principal.getId());
|
document = documentDao.getDocument(documentId, principal.getId());
|
||||||
|
user = userDao.getById(principal.getId());
|
||||||
} catch (NoResultException e) {
|
} catch (NoResultException e) {
|
||||||
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
|
throw new ClientException("DocumentNotFound", MessageFormat.format("Document not found: {0}", documentId));
|
||||||
}
|
}
|
||||||
|
|
||||||
FileDao fileDao = new FileDao();
|
// Keep unencrypted data in memory, because we will need it two times
|
||||||
|
byte[] fileData;
|
||||||
|
try {
|
||||||
|
fileData = ByteStreams.toByteArray(fileBodyPart.getValueAs(InputStream.class));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ServerException("StreamError", "Error reading the input file", e);
|
||||||
|
}
|
||||||
|
InputStream fileInputStream = new ByteArrayInputStream(fileData);
|
||||||
|
|
||||||
// Validate mime type
|
// Validate mime type
|
||||||
InputStream is = new BufferedInputStream(fileBodyPart.getValueAs(InputStream.class));
|
|
||||||
String mimeType;
|
String mimeType;
|
||||||
try {
|
try {
|
||||||
mimeType = MimeTypeUtil.guessMimeType(is);
|
mimeType = MimeTypeUtil.guessMimeType(fileInputStream);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new ServerException("ErrorGuessMime", "Error guessing mime type", e);
|
throw new ServerException("ErrorGuessMime", "Error guessing mime type", e);
|
||||||
}
|
}
|
||||||
@ -113,12 +132,13 @@ public class FileResource extends BaseResource {
|
|||||||
String fileId = fileDao.create(file);
|
String fileId = fileDao.create(file);
|
||||||
|
|
||||||
// Save the file
|
// Save the file
|
||||||
FileUtil.save(is, file);
|
FileUtil.save(fileInputStream, file, user.getPrivateKey());
|
||||||
|
|
||||||
// Raise a new file created event
|
// Raise a new file created event
|
||||||
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
FileCreatedAsyncEvent fileCreatedAsyncEvent = new FileCreatedAsyncEvent();
|
||||||
fileCreatedAsyncEvent.setDocument(document);
|
fileCreatedAsyncEvent.setDocument(document);
|
||||||
fileCreatedAsyncEvent.setFile(file);
|
fileCreatedAsyncEvent.setFile(file);
|
||||||
|
fileCreatedAsyncEvent.setInputStream(fileInputStream);
|
||||||
AppContext.getInstance().getAsyncEventBus().post(fileCreatedAsyncEvent);
|
AppContext.getInstance().getAsyncEventBus().post(fileCreatedAsyncEvent);
|
||||||
|
|
||||||
// Always return ok
|
// Always return ok
|
||||||
@ -288,10 +308,12 @@ public class FileResource extends BaseResource {
|
|||||||
// Get the file
|
// Get the file
|
||||||
FileDao fileDao = new FileDao();
|
FileDao fileDao = new FileDao();
|
||||||
DocumentDao documentDao = new DocumentDao();
|
DocumentDao documentDao = new DocumentDao();
|
||||||
|
UserDao userDao = new UserDao();
|
||||||
File file;
|
File file;
|
||||||
|
Document document;
|
||||||
try {
|
try {
|
||||||
file = fileDao.getFile(fileId);
|
file = fileDao.getFile(fileId);
|
||||||
Document document = documentDao.getDocument(file.getDocumentId());
|
document = documentDao.getDocument(file.getDocumentId());
|
||||||
|
|
||||||
// Check document visibility
|
// Check document visibility
|
||||||
ShareDao shareDao = new ShareDao();
|
ShareDao shareDao = new ShareDao();
|
||||||
@ -304,12 +326,13 @@ public class FileResource extends BaseResource {
|
|||||||
|
|
||||||
|
|
||||||
// Get the stored file
|
// Get the stored file
|
||||||
// TODO Decrypt file
|
|
||||||
java.io.File storedfile;
|
java.io.File storedfile;
|
||||||
String mimeType;
|
String mimeType;
|
||||||
|
boolean decrypt = false;
|
||||||
if (size != null) {
|
if (size != null) {
|
||||||
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId + "_" + size).toFile();
|
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId + "_" + size).toFile();
|
||||||
mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG
|
mimeType = MimeType.IMAGE_JPEG; // Thumbnails are JPEG
|
||||||
|
decrypt = true; // Thumbnails are encrypted
|
||||||
if (!storedfile.exists()) {
|
if (!storedfile.exists()) {
|
||||||
storedfile = new java.io.File(getClass().getResource("/image/file.png").getFile());
|
storedfile = new java.io.File(getClass().getResource("/image/file.png").getFile());
|
||||||
mimeType = MimeType.IMAGE_PNG;
|
mimeType = MimeType.IMAGE_PNG;
|
||||||
@ -317,11 +340,35 @@ public class FileResource extends BaseResource {
|
|||||||
} else {
|
} else {
|
||||||
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId).toFile();
|
storedfile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), fileId).toFile();
|
||||||
mimeType = file.getMimeType();
|
mimeType = file.getMimeType();
|
||||||
|
decrypt = true; // Original files are encrypted
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response.ok(storedfile)
|
// Stream the output and decrypt it if necessary
|
||||||
|
StreamingOutput stream;
|
||||||
|
User user = userDao.getById(document.getUserId());
|
||||||
|
try {
|
||||||
|
InputStream fileInputStream = new FileInputStream(storedfile);
|
||||||
|
final InputStream responseInputStream = decrypt ?
|
||||||
|
EncryptionUtil.decryptInputStream(fileInputStream, user.getPrivateKey()) : fileInputStream;
|
||||||
|
|
||||||
|
stream = new StreamingOutput() {
|
||||||
|
@Override
|
||||||
|
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
|
||||||
|
try {
|
||||||
|
ByteStreams.copy(responseInputStream, outputStream);
|
||||||
|
} finally {
|
||||||
|
responseInputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ServerException("FileError", "Error while reading the file", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response.ok(stream)
|
||||||
.header("Content-Type", mimeType)
|
.header("Content-Type", mimeType)
|
||||||
.header("Expires", new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date().getTime() + 3600000 * 24 * 7))
|
.header("Expires", new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date().getTime() + 3600000 * 24))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||||
version="3.0">
|
version="3.0" metadata-complete="true">
|
||||||
<display-name>Docs</display-name>
|
<display-name>Docs</display-name>
|
||||||
|
|
||||||
|
<absolute-ordering></absolute-ordering>
|
||||||
|
|
||||||
<!-- This filter is used to secure URLs -->
|
<!-- This filter is used to secure URLs -->
|
||||||
<filter>
|
<filter>
|
||||||
<filter-name>requestContextFilter</filter-name>
|
<filter-name>requestContextFilter</filter-name>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<img src="img/loader.gif" ng-if="!document && isEdit()" />
|
<img src="img/loader.gif" ng-show="!document && isEdit()" />
|
||||||
|
|
||||||
<div ng-if="document || !isEdit()">
|
<div ng-show="document || !isEdit()">
|
||||||
<form class="form-horizontal" name="documentForm">
|
<form class="form-horizontal" name="documentForm">
|
||||||
<div class="control-group" ng-class="{ error: !documentForm.title.$valid }">
|
<div class="control-group" ng-class="{ error: !documentForm.title.$valid }">
|
||||||
<label class="control-label" for="inputTitle">Title</label>
|
<label class="control-label" for="inputTitle">Title</label>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<img src="img/loader.gif" ng-if="!document" />
|
<img src="img/loader.gif" ng-show="!document" />
|
||||||
|
|
||||||
<div ng-if="document">
|
<div ng-show="document">
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-danger" ng-click="deleteDocument(document)"><span class="icon-trash icon-white"></span> Delete</button>
|
<button class="btn btn-danger" ng-click="deleteDocument(document)"><span class="icon-trash icon-white"></span> Delete</button>
|
||||||
@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h1>{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small> <img ng-src="img/flag/{{ document.language }}.png" title="{{ document.language }}" /></h1>
|
<h1>{{ document.title }} <small>{{ document.create_date | date: 'yyyy-MM-dd' }}</small> <img ng-if="document" ng-src="img/flag/{{ document.language }}.png" title="{{ document.language }}" /></h1>
|
||||||
<p>
|
<p>
|
||||||
<button class="btn btn-small btn-inverse" ng-click="share()"><span class="icon-share icon-white"></span> Share</button>
|
<button class="btn btn-small btn-inverse" ng-click="share()"><span class="icon-share icon-white"></span> Share</button>
|
||||||
<button class="btn btn-small" ng-repeat="share in document.shares" ng-click="showShare(share)"><span class="icon-ok"></span> {{ share.name ? share.name : 'shared' }}</button>
|
<button class="btn btn-small" ng-repeat="share in document.shares" ng-click="showShare(share)"><span class="icon-ok"></span> {{ share.name ? share.name : 'shared' }}</button>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<img src="img/loader.gif" ng-if="!user && isEdit()" />
|
<img src="img/loader.gif" ng-show="!user && isEdit()" />
|
||||||
|
|
||||||
<div ng-if="user || !isEdit()">
|
<div ng-show="user || !isEdit()">
|
||||||
<h2 ng-show="isEdit()">Edit
|
<h2 ng-show="isEdit()">Edit
|
||||||
<small>"{{ user.username }}"</small>
|
<small>"{{ user.username }}"</small>
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -43,13 +43,6 @@ public class TestAppResource extends BaseJerseyTest {
|
|||||||
Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory);
|
Assert.assertTrue(totalMemory > 0 && totalMemory > freeMemory);
|
||||||
Assert.assertEquals(0, json.getInt("document_count"));
|
Assert.assertEquals(0, json.getInt("document_count"));
|
||||||
|
|
||||||
// OCR-ize all files
|
|
||||||
appResource = resource().path("/app/batch/extract");
|
|
||||||
appResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
|
||||||
response = appResource.post(ClientResponse.class);
|
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
|
||||||
json = response.getEntity(JSONObject.class);
|
|
||||||
|
|
||||||
// Rebuild Lucene index
|
// Rebuild Lucene index
|
||||||
appResource = resource().path("/app/batch/reindex");
|
appResource = resource().path("/app/batch/reindex");
|
||||||
appResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
appResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.sismics.docs.rest;
|
package com.sismics.docs.rest;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
@ -15,6 +16,8 @@ import org.junit.Test;
|
|||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.sismics.docs.core.util.DirectoryUtil;
|
import com.sismics.docs.core.util.DirectoryUtil;
|
||||||
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
import com.sismics.docs.rest.filter.CookieAuthenticationFilter;
|
||||||
|
import com.sismics.util.mime.MimeType;
|
||||||
|
import com.sismics.util.mime.MimeTypeUtil;
|
||||||
import com.sun.jersey.api.client.ClientResponse;
|
import com.sun.jersey.api.client.ClientResponse;
|
||||||
import com.sun.jersey.api.client.ClientResponse.Status;
|
import com.sun.jersey.api.client.ClientResponse.Status;
|
||||||
import com.sun.jersey.api.client.WebResource;
|
import com.sun.jersey.api.client.WebResource;
|
||||||
@ -88,6 +91,7 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||||
InputStream is = response.getEntityInputStream();
|
InputStream is = response.getEntityInputStream();
|
||||||
byte[] fileBytes = ByteStreams.toByteArray(is);
|
byte[] fileBytes = ByteStreams.toByteArray(is);
|
||||||
|
Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes));
|
||||||
Assert.assertEquals(163510, fileBytes.length);
|
Assert.assertEquals(163510, fileBytes.length);
|
||||||
|
|
||||||
// Get the thumbnail data
|
// Get the thumbnail data
|
||||||
@ -99,6 +103,7 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||||
is = response.getEntityInputStream();
|
is = response.getEntityInputStream();
|
||||||
fileBytes = ByteStreams.toByteArray(is);
|
fileBytes = ByteStreams.toByteArray(is);
|
||||||
|
Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes));
|
||||||
Assert.assertEquals(41935, fileBytes.length);
|
Assert.assertEquals(41935, fileBytes.length);
|
||||||
|
|
||||||
// Get the web data
|
// Get the web data
|
||||||
@ -110,45 +115,14 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
||||||
is = response.getEntityInputStream();
|
is = response.getEntityInputStream();
|
||||||
fileBytes = ByteStreams.toByteArray(is);
|
fileBytes = ByteStreams.toByteArray(is);
|
||||||
|
Assert.assertEquals(MimeType.IMAGE_JPEG, MimeTypeUtil.guessMimeType(fileBytes));
|
||||||
Assert.assertEquals(551084, fileBytes.length);
|
Assert.assertEquals(551084, fileBytes.length);
|
||||||
|
|
||||||
// Regenerate file variations
|
// Check that the files are not readable directly from FS
|
||||||
String adminAuthenticationToken = clientUtil.login("admin", "admin", false);
|
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
||||||
WebResource appResource = resource().path("/app/batch/file_variations");
|
InputStream storedFileInputStream = new BufferedInputStream(new FileInputStream(storedFile));
|
||||||
appResource.addFilter(new CookieAuthenticationFilter(adminAuthenticationToken));
|
Assert.assertNull(MimeTypeUtil.guessMimeType(storedFileInputStream));
|
||||||
response = appResource.post(ClientResponse.class);
|
storedFileInputStream.close();
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
|
||||||
|
|
||||||
// Get the file data
|
|
||||||
fileResource = resource().path("/file/" + file1Id + "/data");
|
|
||||||
fileResource.addFilter(new CookieAuthenticationFilter(file1AuthenticationToken));
|
|
||||||
response = fileResource.get(ClientResponse.class);
|
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
|
||||||
is = response.getEntityInputStream();
|
|
||||||
fileBytes = ByteStreams.toByteArray(is);
|
|
||||||
Assert.assertEquals(163510, fileBytes.length);
|
|
||||||
|
|
||||||
// Get the thumbnail data
|
|
||||||
fileResource = resource().path("/file/" + file1Id + "/data");
|
|
||||||
fileResource.addFilter(new CookieAuthenticationFilter(file1AuthenticationToken));
|
|
||||||
getParams = new MultivaluedMapImpl();
|
|
||||||
getParams.putSingle("size", "thumb");
|
|
||||||
response = fileResource.queryParams(getParams).get(ClientResponse.class);
|
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
|
||||||
is = response.getEntityInputStream();
|
|
||||||
fileBytes = ByteStreams.toByteArray(is);
|
|
||||||
Assert.assertEquals(41935, fileBytes.length);
|
|
||||||
|
|
||||||
// Get the web data
|
|
||||||
fileResource = resource().path("/file/" + file1Id + "/data");
|
|
||||||
fileResource.addFilter(new CookieAuthenticationFilter(file1AuthenticationToken));
|
|
||||||
getParams = new MultivaluedMapImpl();
|
|
||||||
getParams.putSingle("size", "web");
|
|
||||||
response = fileResource.queryParams(getParams).get(ClientResponse.class);
|
|
||||||
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
|
|
||||||
is = response.getEntityInputStream();
|
|
||||||
fileBytes = ByteStreams.toByteArray(is);
|
|
||||||
Assert.assertEquals(551084, fileBytes.length);
|
|
||||||
|
|
||||||
// Get all files from a document
|
// Get all files from a document
|
||||||
fileResource = resource().path("/file/list");
|
fileResource = resource().path("/file/list");
|
||||||
@ -195,7 +169,7 @@ public class TestFileResource extends BaseJerseyTest {
|
|||||||
Assert.assertEquals("ok", json.getString("status"));
|
Assert.assertEquals("ok", json.getString("status"));
|
||||||
|
|
||||||
// Check that files are deleted from FS
|
// Check that files are deleted from FS
|
||||||
java.io.File storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
storedFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id).toFile();
|
||||||
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile();
|
java.io.File webFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_web").toFile();
|
||||||
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile();
|
java.io.File thumbnailFile = Paths.get(DirectoryUtil.getStorageDirectory().getPath(), file1Id + "_thumb").toFile();
|
||||||
Assert.assertFalse(storedFile.exists());
|
Assert.assertFalse(storedFile.exists());
|
||||||
|
Loading…
Reference in New Issue
Block a user