diff --git a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java index 3347bb51b797231e3d58d8ba47f864130e09c511..3cf06bf971586130f1ea394c46a147367e2cfefe 100644 --- a/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java +++ b/api-data-store-server-java/src/main/java/ch/ethz/sis/afsclient/client/AfsClient.java @@ -42,8 +42,6 @@ public final class AfsClient implements PublicAPI, ClientAPI private static final int DEFAULT_TIMEOUT_IN_MILLIS = 30000; - private static final String MD5 = "MD5"; - private final int maxReadSizeInBytes; private final int timeout; @@ -361,7 +359,18 @@ public final class AfsClient implements PublicAPI, ClientAPI { try { - return MessageDigest.getInstance(MD5).digest(data); + return MessageDigest.getInstance("MD5").digest(data); + } catch (Exception exception) + { + throw new RuntimeException(exception); + } + } + + private static byte[] getSHA1(final byte[] data) + { + try + { + return MessageDigest.getInstance("SHA-1").digest(data); } catch (Exception exception) { throw new RuntimeException(exception); @@ -592,7 +601,7 @@ public final class AfsClient implements PublicAPI, ClientAPI final byte[] data = result < fullBuffer.length ? Arrays.copyOf(fullBuffer, result) : fullBuffer; try { - final Boolean writeSuccessful = write(owner, destination, this.offset, data, getMD5(data)); + final Boolean writeSuccessful = write(owner, destination, this.offset, data, getSHA1(data)); if (!writeSuccessful) { hasError.set(true); diff --git a/api-data-store-server-javascript/src/js/api/server-data-store-facade.js b/api-data-store-server-javascript/src/js/api/server-data-store-facade.js index 2cb686c301b3ddf803b263c1b901cff6cdee0550..943f0bcbc88a651d8d9dd5a4819535566e8a8c86 100644 --- a/api-data-store-server-javascript/src/js/api/server-data-store-facade.js +++ b/api-data-store-server-javascript/src/js/api/server-data-store-facade.js @@ -439,16 +439,17 @@ function hex2a(hexx) { * @param {str} owner owner of the file * @param {str} source path to file * @param {int} offset offset from which to start writing - * @param {str} dataBase64 data to write in base64 format + * @param {str} base64Data data to write in base64 format + * @param {str} base64Hash MD5 of the base64 data */ -DataStoreServer.prototype.write = function(owner, source, offset, dataBase64){ +DataStoreServer.prototype.write = function(owner, source, offset, base64Data, base64Hash){ const params = this.fillCommonParameters({ "method": "write", "owner" : owner, "source": source, "offset": offset, - "data": dataBase64, - "md5Hash": btoa(hex2a(md5(atob(dataBase64)))), + "data": base64Data, + "md5Hash": base64Hash }); return this._internal.sendHttpRequest( diff --git a/api-data-store-server-javascript/src/js/demo/server-data-store-client.js b/api-data-store-server-javascript/src/js/demo/server-data-store-client.js index 2fee6a53822434e33d21eee7575ab0ee4a1b3874..8528af8c83a03683698d36d1f8703b02ced69250 100644 --- a/api-data-store-server-javascript/src/js/demo/server-data-store-client.js +++ b/api-data-store-server-javascript/src/js/demo/server-data-store-client.js @@ -193,7 +193,7 @@ window.onload = function() { datastoreServer.write(owner, document.getElementById("fpath").value.trim(), parseInt(document.getElementById("foffset").value.trim()), - btoa(document.getElementById("write-text").value.trim())) + document.getElementById("write-text").value.trim()) .then(() => showEntries()); } }; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java index 7ac51069f5176d05331d393e93fb6c75dda691cc..25ae7e158f6cd8a9a8fe7c0e9733c05433c112eb 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/api/OperationsAPI.java @@ -29,7 +29,7 @@ public interface OperationsAPI { @NonNull byte[] read(@NonNull String source, @NonNull long offset, @NonNull int limit) throws Exception; - boolean write(@NonNull String source, @NonNull long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception; + boolean write(@NonNull String source, @NonNull long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception; boolean delete(@NonNull String source) throws Exception; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java index 8048ff351d37632c05270bb70b99ad9aa4d43734..24ac74e6c319ac8c03832b56692d0aec07eb26f4 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/dto/operation/WriteOperation.java @@ -36,17 +36,17 @@ public class WriteOperation implements Operation { private String tempSource; private long offset; private byte[] data; - private byte[] md5Hash; + private byte[] hash; private OperationName name; - public WriteOperation(UUID owner, String source, String tempSource, long offset, byte[] data, byte[] md5Hash) { + public WriteOperation(UUID owner, String source, String tempSource, long offset, byte[] data, byte[] hash) { this.owner = owner; this.locks = List.of(new Lock<>(owner, source, LockType.Exclusive)); this.source = source; this.tempSource = tempSource; this.offset = offset; this.data = data; - this.md5Hash = md5Hash; + this.hash = hash; this.name = OperationName.Write; } } diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java index c6a89190e62ca544bfb3876e7d5aa3d55416f035..e209906e1825b9ef70269ab53d3668aeab4af7b8 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/exception/AFSExceptions.java @@ -40,7 +40,7 @@ public enum AFSExceptions implements ExceptionTemplateHolder { PathInStore( RuntimeException.class, List.of(ClientDeveloperCodingError),10015,"Path given to: %s - In store: %s"), PathInStoreCantBeRelative( RuntimeException.class, List.of(ClientDeveloperCodingError),10016,"Path given to: %s - can't contain '/../': %s"), PathNotStartWithRoot( RuntimeException.class, List.of(ClientDeveloperCodingError),10017,"Path given to: %s - don't starts with root '/' : %s"), - MD5NotMatch( RuntimeException.class, List.of(ClientDeveloperCodingError),10018,"MD5 doesn't match on data given to: %s - for: %s"), + HashNotMatch( RuntimeException.class, List.of(ClientDeveloperCodingError),10018,"Hash does not match data given to: %s - for: %s"), DeadlockDetected( RuntimeException.class, List.of(UserUsageError), 10019,"Deadlock detected, %s is already waiting for %s from %s"), TransactionReuse( RuntimeException.class, List.of(CoreDeveloperCodingError), 10020,"Transaction with uuid: %s and state: %s was going to be reused"); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java index a02572ce815e1650f02a29149aba951e5321fb2c..6c5d9c32015218b6995362822c333de395b3b825 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/TransactionConnection.java @@ -284,10 +284,10 @@ public class TransactionConnection implements TransactionalFileSystem { private final Set<String> written = new HashSet<>(); @Override - public boolean write(String source, long offset, byte[] data, byte[] md5Hash) throws Exception { + public boolean write(String source, long offset, byte[] data, byte[] hash) throws Exception { source = getSafePath(OperationName.Write, source); String tempSource = OperationExecutor.getTempPath(transaction, source) + "." + UUID.randomUUID(); - WriteOperation operation = new WriteOperation(transaction.getUuid(), source, tempSource, offset, data, md5Hash); + WriteOperation operation = new WriteOperation(transaction.getUuid(), source, tempSource, offset, data, hash); boolean prepared = prepare(operation, source, null); if (prepared) { written.add(source); @@ -380,7 +380,7 @@ public class TransactionConnection implements TransactionalFileSystem { if (prepared) { if (Objects.requireNonNull(operationName) == OperationName.Write) { - transaction.getOperations().add(((WriteOperation) operation).toBuilder().data(null).md5Hash(null).build()); + transaction.getOperations().add(((WriteOperation) operation).toBuilder().data(null).hash(null).build()); } else { transaction.getOperations().add(operation); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java index 1d690946d2943f508bd5ab95ee38ed22a9e245bb..ec852f4be0ddc5f45a9a1e03f5d4fbf645eaab34 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/CreateOperationExecutor.java @@ -15,17 +15,11 @@ */ package ch.ethz.sis.afs.manager.operation; -import static ch.ethz.sis.afs.exception.AFSExceptions.MD5NotMatch; import static ch.ethz.sis.afs.exception.AFSExceptions.PathInStore; -import static ch.ethz.sis.afs.exception.AFSExceptions.PathIsDirectory; -import java.util.Arrays; - -import ch.ethz.sis.afs.api.dto.File; import ch.ethz.sis.afs.dto.Transaction; import ch.ethz.sis.afs.dto.operation.CreateOperation; import ch.ethz.sis.afs.dto.operation.OperationName; -import ch.ethz.sis.afs.dto.operation.WriteOperation; import ch.ethz.sis.afs.exception.AFSExceptions; import ch.ethz.sis.shared.io.IOUtils; import lombok.NonNull; diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java index 97ab162e88a40f28b274339ce380288a4bc93515..a653ca089cf0c8280e792d980c93fdda17c08a7b 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/afs/manager/operation/WriteOperationExecutor.java @@ -15,7 +15,7 @@ */ package ch.ethz.sis.afs.manager.operation; -import static ch.ethz.sis.afs.exception.AFSExceptions.MD5NotMatch; +import static ch.ethz.sis.afs.exception.AFSExceptions.HashNotMatch; import static ch.ethz.sis.afs.exception.AFSExceptions.PathIsDirectory; import java.util.Arrays; @@ -63,9 +63,9 @@ public class WriteOperationExecutor implements OperationExecutor<WriteOperation> } // 1. Validate new data - byte[] md5Hash = IOUtils.getMD5(operation.getData()); - if (!Arrays.equals(md5Hash, operation.getMd5Hash())) { - AFSExceptions.throwInstance(MD5NotMatch, OperationName.Write.name(), operation.getSource()); + byte[] hash = IOUtils.getSHA1(operation.getData()); + if (!Arrays.equals(hash, operation.getHash())) { + AFSExceptions.throwInstance(HashNotMatch, OperationName.Write.name(), operation.getSource()); } // 2. Create temporary file if it has not been created already boolean tempSourceExists = IOUtils.exists(operation.getTempSource()); diff --git a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java b/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java index ee574c4219d8ea24c7baace898e8d99be8a073b5..f594ac6d9a661e70ef6f31d3d3e3bd23d0a53bd5 100644 --- a/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java +++ b/lib-transactional-file-system/src/main/java/ch/ethz/sis/shared/io/IOUtils.java @@ -620,11 +620,17 @@ public class IOUtils { return pathAStore.equals(pathBStore); } - private static final String MD5 = "MD5"; - public static byte[] getMD5(byte[] data) { try { - return MessageDigest.getInstance(MD5).digest(data); + return MessageDigest.getInstance("MD5").digest(data); + } catch (Exception exception) { + throw new RuntimeException(exception); + } + } + + public static byte[] getSHA1(byte[] data) { + try { + return MessageDigest.getInstance("SHA-1").digest(data); } catch (Exception exception) { throw new RuntimeException(exception); } diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java index 6e075316ec5f3fcb499e851866674cdcf3832ed0..aa1c65c7c3708349b032501425b6ca066090409d 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/AbstractProxy.java @@ -144,8 +144,8 @@ public abstract class AbstractProxy implements Worker<TransactionalFileSystem> { } @Override - public Boolean write(@NonNull String sourceOwner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { - return nextProxy.write(sourceOwner, source, offset, data, md5Hash); + public Boolean write(@NonNull String sourceOwner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { + return nextProxy.write(sourceOwner, source, offset, data, hash); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java index a9c0fafb1f883693b82d44de6a0c33d41356300b..1cb9d071020fda6b8e2b330eeefbd0a57999b307 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuditorProxy.java @@ -94,9 +94,9 @@ public class AuditorProxy extends AbstractProxy { } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { auditBefore(); - return auditAfter(nextProxy.write(owner, source, offset, data, md5Hash)); + return auditAfter(nextProxy.write(owner, source, offset, data, hash)); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java index af3ca09d9bee2504d9e471b1f4ad9e20cdaca690..985b22cc6e17e4e62345cc729f4531897e98419e 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthenticationProxy.java @@ -116,9 +116,9 @@ public class AuthenticationProxy extends AbstractProxy { } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { validateSessionAvailable(); - return nextProxy.write(owner, source, offset, data, md5Hash); + return nextProxy.write(owner, source, offset, data, hash); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java index 7790fa79bacf877222a57d194c54511e4aa8ae04..a20473747e3af999fb7c633d299da900dfc252ad 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/AuthorizationProxy.java @@ -60,9 +60,9 @@ public class AuthorizationProxy extends AbstractProxy { } @Override - public Boolean write(String owner, String source, Long offset, byte[] data, byte[] md5Hash) throws Exception { + public Boolean write(String owner, String source, Long offset, byte[] data, byte[] hash) throws Exception { validateUserRights(owner, source, IOUtils.writePermissions, OperationName.Write); - return nextProxy.write(owner, source, offset, data, md5Hash); + return nextProxy.write(owner, source, offset, data, hash); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java index d2c6eeac7a6bd6843d8c68ac10770b4370fea1a5..da76ff85b3ff4a18fde90ffa27b22f9eb5a307f5 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/LogProxy.java @@ -79,9 +79,9 @@ public class LogProxy extends AbstractProxy { } @Override - public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] md5Hash) throws Exception { - logger.traceAccess(null, owner, source, offset, data.length, md5Hash.length); - return logger.traceExit(nextProxy.write(owner, source, offset, data, md5Hash)); + public Boolean write(@NonNull String owner, @NonNull String source, @NonNull Long offset, @NonNull byte[] data, @NonNull byte[] hash) throws Exception { + logger.traceAccess(null, owner, source, offset, data.length, hash.length); + return logger.traceExit(nextProxy.write(owner, source, offset, data, hash)); } @Override diff --git a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java index 42007bf196aa04973fdf66f42d4ce565fdbdfdcb..25f378c23e0a9c4d0b7984210c34c00860c38d4d 100644 --- a/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java +++ b/server-data-store/src/main/java/ch/ethz/sis/afsserver/worker/proxy/ValidationProxy.java @@ -45,8 +45,8 @@ public class ValidationProxy extends AbstractProxy { } @Override - public Boolean write(String owner, String source, Long offset, byte[] data, byte[] md5Hash) throws Exception { - return nextProxy.write(owner, source, offset, data, md5Hash); + public Boolean write(String owner, String source, Long offset, byte[] data, byte[] hash) throws Exception { + return nextProxy.write(owner, source, offset, data, hash); } @Override diff --git a/ui-admin/index.html b/ui-admin/index.html index 0ad80de41a1496f72d7f8ca8273abd5d678bda55..db5bd694f559cddabcc4dc2a0d82a8d38a73f052 100644 --- a/ui-admin/index.html +++ b/ui-admin/index.html @@ -40,7 +40,6 @@ onerror="loadError()" ></script> <script src="/openbis/resources/api/v3/require.js"></script> - <script src="/openbis/resources/api/data-store-server/api/md5.js"></script> <script src="/openbis/resources/api/data-store-server/api/server-data-store-facade.js"></script> </head> <body> diff --git a/ui-admin/src/js/components/database/data-browser/DataBrowserController.js b/ui-admin/src/js/components/database/data-browser/DataBrowserController.js index 92d1d0e20e87f550a19575ececaf6473b1e9d41d..d893a3730b8e6645984f0c5abcf2668e8b1f783e 100644 --- a/ui-admin/src/js/components/database/data-browser/DataBrowserController.js +++ b/ui-admin/src/js/components/database/data-browser/DataBrowserController.js @@ -169,27 +169,29 @@ export default class DataBrowserController extends ComponentController { while (offset < file.size) { const chunkData = await file.slice(offset, offset + CHUNK_SIZE).arrayBuffer() // console.log(`Uploading chunk: ${offset} - Size: ${chunkData.byteLength}`) - await this._uploadChunk(file.name, offset, await this._arrayBufferToBase64(chunkData)) + await this._uploadChunk(file.name, offset, chunkData) offset += CHUNK_SIZE } } async _uploadChunk(source, offset, data) { - // console.log(data) - return await this.component.datastoreServer.write(this.owner, source, offset, data) + const hash = await crypto.subtle.digest("SHA-1", data) + const base64Data = await this._arrayBufferToBase64(data) + const base64Hash = await this._arrayBufferToBase64(hash) + + return await this.component.datastoreServer.write(this.owner, source, offset, base64Data, base64Hash) } async _arrayBufferToBase64(buffer) { return new Promise((resolve, reject) => { - const blob = new Blob([buffer], {type: 'application/octet-stream'}) - const reader = new FileReader() + const blob = new Blob([buffer]); + const reader = new FileReader(); reader.onloadend = () => { - const dataUrl = reader.result - const base64String = dataUrl.split(',')[1] - resolve(base64String) + const base64data = reader.result.split(',')[1]; + resolve(base64data); }; - reader.onerror = reject - reader.readAsDataURL(blob) + reader.onerror = reject; + reader.readAsDataURL(blob); }); }