mirror of
https://github.com/iterate-ch/cyberduck.git
synced 2026-05-26 19:10:49 +00:00
Merge branch 'master' into feature/GH-17459
This commit is contained in:
@@ -15,7 +15,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ linux-self-hosted, macos-self-hosted, windows-self-hosted ]
|
||||
os: [ linux-self-hosted ]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Set up JDK 21
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
* [Bugfix] Browser window does not show up in Exposé & Mission Control (macOS) ([#17703](https://trac.cyberduck.io/ticket/17703))
|
||||
* [Bugfix] Compare public key blob instead of comment when retrieving key from agent (SFTP)
|
||||
* [Bugfix] Support "Include" directive when reading from OpenSSH config (SFTP) ([#10451](https://trac.cyberduck.io/ticket/10451))
|
||||
* [Bugfix] Exclude trashed folders in list by default (Backblaze B2) ([#18101](https://trac.cyberduck.io/ticket/18101))
|
||||
|
||||
[9.4.1](https://github.com/iterate-ch/cyberduck/compare/release-9-4-0...release-9-4-1)
|
||||
* [Bugfix] Cleartext uploads to unlocked vault with auto detect disabled in Preferences (
|
||||
|
||||
@@ -17,6 +17,7 @@ package ch.cyberduck.core.b2;
|
||||
|
||||
import ch.cyberduck.core.AttributedList;
|
||||
import ch.cyberduck.core.DefaultIOExceptionMappingService;
|
||||
import ch.cyberduck.core.DefaultPathAttributes;
|
||||
import ch.cyberduck.core.DefaultPathContainerService;
|
||||
import ch.cyberduck.core.ListProgressListener;
|
||||
import ch.cyberduck.core.ListService;
|
||||
@@ -28,15 +29,27 @@ import ch.cyberduck.core.VersioningConfiguration;
|
||||
import ch.cyberduck.core.exception.BackgroundException;
|
||||
import ch.cyberduck.core.exception.NotfoundException;
|
||||
import ch.cyberduck.core.preferences.HostPreferencesFactory;
|
||||
import ch.cyberduck.core.threading.BackgroundExceptionCallable;
|
||||
import ch.cyberduck.core.threading.ThreadPool;
|
||||
import ch.cyberduck.core.threading.ThreadPoolFactory;
|
||||
import ch.cyberduck.core.worker.DefaultExceptionMappingService;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.util.concurrent.Uninterruptibles;
|
||||
|
||||
import synapticloop.b2.Action;
|
||||
import synapticloop.b2.exception.B2ApiException;
|
||||
@@ -69,6 +82,7 @@ public class B2ObjectListService implements ListService {
|
||||
|
||||
@Override
|
||||
public AttributedList<Path> list(final Path directory, final ListProgressListener listener) throws BackgroundException {
|
||||
final ThreadPool pool = ThreadPoolFactory.get("list", HostPreferencesFactory.get(session.getHost()).getInteger("b2.listing.concurrency"));
|
||||
try {
|
||||
final AttributedList<Path> objects = new AttributedList<>();
|
||||
Marker marker = new Marker(null, null);
|
||||
@@ -93,7 +107,20 @@ public class B2ObjectListService implements ListService {
|
||||
this.createPrefix(directory),
|
||||
String.valueOf(Path.DELIMITER));
|
||||
}
|
||||
marker = this.parse(directory, objects, response, revisions);
|
||||
final List<Future<Path>> folders = new ArrayList<>();
|
||||
marker = this.parse(directory, objects, response, revisions, containerId, pool, folders);
|
||||
for(Future<Path> f : folders) {
|
||||
try {
|
||||
objects.add(Uninterruptibles.getUninterruptibly(f));
|
||||
}
|
||||
catch(ExecutionException e) {
|
||||
log.warn("Listing versioned objects failed with execution failure {}", e.getMessage());
|
||||
for(Throwable cause : ExceptionUtils.getThrowableList(e)) {
|
||||
Throwables.throwIfInstanceOf(cause, BackgroundException.class);
|
||||
}
|
||||
throw new DefaultExceptionMappingService().map(Throwables.getRootCause(e));
|
||||
}
|
||||
}
|
||||
if(null == marker.nextFileId) {
|
||||
if(!response.getFiles().isEmpty()) {
|
||||
hasDirectoryPlaceholder = true;
|
||||
@@ -114,6 +141,9 @@ public class B2ObjectListService implements ListService {
|
||||
catch(IOException e) {
|
||||
throw new DefaultIOExceptionMappingService().map(e);
|
||||
}
|
||||
finally {
|
||||
pool.shutdown(false);
|
||||
}
|
||||
}
|
||||
|
||||
private String createPrefix(final Path directory) {
|
||||
@@ -122,7 +152,8 @@ public class B2ObjectListService implements ListService {
|
||||
}
|
||||
|
||||
private Marker parse(final Path directory, final AttributedList<Path> objects,
|
||||
final B2ListFilesResponse response, final Map<String, Long> revisions) {
|
||||
final B2ListFilesResponse response, final Map<String, Long> revisions,
|
||||
final String containerId, final ThreadPool pool, final List<Future<Path>> folders) {
|
||||
final B2AttributesFinderFeature attr = new B2AttributesFinderFeature(session, fileid);
|
||||
for(B2FileInfoResponse info : response.getFiles()) {
|
||||
if(StringUtils.equals(PathNormalizer.name(info.getFileName()), B2PathContainerService.PLACEHOLDER)) {
|
||||
@@ -136,10 +167,15 @@ public class B2ObjectListService implements ListService {
|
||||
}
|
||||
if(StringUtils.isBlank(info.getFileId())) {
|
||||
// Common prefix
|
||||
final Path placeholder = new Path(directory.isDirectory() ? directory : directory.getParent(),
|
||||
PathNormalizer.name(StringUtils.removeEnd(info.getFileName(), String.valueOf(Path.DELIMITER))),
|
||||
EnumSet.of(Path.Type.directory, Path.Type.placeholder));
|
||||
objects.add(placeholder);
|
||||
if(versioning.isEnabled()) {
|
||||
// Determine trashed state asynchronously by checking for live content beneath the prefix
|
||||
folders.add(this.submit(pool, directory, containerId, info.getFileName()));
|
||||
}
|
||||
else {
|
||||
objects.add(new Path(directory.isDirectory() ? directory : directory.getParent(),
|
||||
PathNormalizer.name(StringUtils.removeEnd(info.getFileName(), String.valueOf(Path.DELIMITER))),
|
||||
EnumSet.of(Path.Type.directory, Path.Type.placeholder)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
final PathAttributes attributes = attr.toAttributes(info);
|
||||
@@ -164,6 +200,44 @@ public class B2ObjectListService implements ListService {
|
||||
return new Marker(response.getNextFileName(), response.getNextFileId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine path from prefix. Path will have trashed attribute set when no live (non-hidden) content
|
||||
* exists beneath the prefix.
|
||||
*
|
||||
* @param pool Thread pool to run task with
|
||||
* @param directory The directory for which contents are listed
|
||||
* @param containerId B2 bucket ID
|
||||
* @param prefix Common prefix found in directory listing (ends with delimiter)
|
||||
* @return Path to add to directory list
|
||||
*/
|
||||
private Future<Path> submit(final ThreadPool pool, final Path directory, final String containerId, final String prefix) {
|
||||
return pool.execute(new BackgroundExceptionCallable<Path>() {
|
||||
@Override
|
||||
public Path call() throws BackgroundException {
|
||||
try {
|
||||
final PathAttributes folderAttributes = new DefaultPathAttributes();
|
||||
// Query without delimiter to check recursively for any live files beneath this prefix.
|
||||
// Hide markers are excluded by listFileNames, so an empty result means all content is deleted.
|
||||
final B2ListFilesResponse liveContent = session.getClient().listFileNames(
|
||||
containerId, null, 1, prefix, null);
|
||||
if(liveContent.getFiles().isEmpty()) {
|
||||
log.debug("Set trashed attribute for prefix {}", prefix);
|
||||
folderAttributes.setTrashed(true);
|
||||
}
|
||||
return new Path(directory.isDirectory() ? directory : directory.getParent(),
|
||||
PathNormalizer.name(StringUtils.removeEnd(prefix, String.valueOf(Path.DELIMITER))),
|
||||
EnumSet.of(Path.Type.directory, Path.Type.placeholder), folderAttributes);
|
||||
}
|
||||
catch(B2ApiException e) {
|
||||
throw new B2ExceptionMappingService(fileid).map("Listing directory {0} failed", e, directory);
|
||||
}
|
||||
catch(IOException e) {
|
||||
throw new DefaultIOExceptionMappingService().map(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static final class Marker {
|
||||
public final String nextFilename;
|
||||
public final String nextFileId;
|
||||
|
||||
@@ -412,9 +412,13 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
|
||||
}
|
||||
// Nullify version to add delete marker
|
||||
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(file.withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(null))), LoginCallback.noop, new Delete.DisabledCallback());
|
||||
assertTrue(new DefaultFindFeature(session).find(folder1, new DisabledListProgressListener()));
|
||||
assertTrue(new B2ObjectListService(session, fileid).list(folder1, new DisabledListProgressListener()).contains(folder2));
|
||||
assertTrue(new DefaultFindFeature(session).find(folder2, new DisabledListProgressListener()));
|
||||
assertFalse(new DefaultFindFeature(session).find(folder1, new DisabledListProgressListener()));
|
||||
final AttributedList<Path> list = new B2ObjectListService(session, fileid).list(folder1, new DisabledListProgressListener());
|
||||
assertTrue(list.contains(folder2));
|
||||
for(Path f : list) {
|
||||
assertTrue(f.attributes().isTrashed());
|
||||
}
|
||||
assertFalse(new DefaultFindFeature(session).find(folder2, new DisabledListProgressListener()));
|
||||
assertEquals(2, new B2ObjectListService(session, fileid).list(folder2, new DisabledListProgressListener()).size());
|
||||
assertThrows(NotfoundException.class, () -> new B2ObjectListService(session, fileid, 1, VersioningConfiguration.empty()).list(folder2, new DisabledListProgressListener()));
|
||||
for(Path f : new B2ObjectListService(session, fileid).list(folder2, new DisabledListProgressListener())) {
|
||||
@@ -438,6 +442,79 @@ public class B2ObjectListServiceTest extends AbstractB2Test {
|
||||
new B2DeleteFeature(session, fileid).delete(Arrays.asList(file1, folder1, bucket), LoginCallback.noop, new Delete.DisabledCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListFolderCreateModifyDelete() throws Exception {
|
||||
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
|
||||
final Path bucket = new B2DirectoryFeature(session, fileid).mkdir(
|
||||
new B2WriteFeature(session, fileid),
|
||||
new Path(String.format("test-%s", new AsciiRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)),
|
||||
new TransferStatus());
|
||||
// Create folder
|
||||
final Path folder = new B2DirectoryFeature(session, fileid).mkdir(
|
||||
new B2WriteFeature(session, fileid),
|
||||
new Path(bucket, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)),
|
||||
new TransferStatus());
|
||||
// Create file in folder
|
||||
final Path file = new Path(folder, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file));
|
||||
{
|
||||
final byte[] content = RandomUtils.nextBytes(32);
|
||||
final TransferStatus status = new TransferStatus().setLength(content.length);
|
||||
status.setChecksum(new SHA1ChecksumCompute().compute(new ByteArrayInputStream(content), status));
|
||||
final HttpResponseOutputStream<BaseB2Response> out = new B2WriteFeature(session, fileid).write(file, status, ConnectionCallback.noop);
|
||||
IOUtils.write(content, out);
|
||||
out.close();
|
||||
file.attributes().setVersionId(((B2FileResponse) out.getStatus()).getFileId());
|
||||
}
|
||||
// Versioned listing: file and folder appear (1 version each)
|
||||
assertTrue(new B2ObjectListService(session, fileid, 10, new VersioningConfiguration(true)).list(folder, new DisabledListProgressListener()).contains(file));
|
||||
assertTrue(new B2ObjectListService(session, fileid, 10, new VersioningConfiguration(true)).list(bucket, new DisabledListProgressListener()).contains(folder));
|
||||
// Modify file (overwrite with new content)
|
||||
{
|
||||
final byte[] content = RandomUtils.nextBytes(64);
|
||||
final TransferStatus status = new TransferStatus().setLength(content.length);
|
||||
status.setChecksum(new SHA1ChecksumCompute().compute(new ByteArrayInputStream(content), status));
|
||||
final HttpResponseOutputStream<BaseB2Response> out = new B2WriteFeature(session, fileid).write(file, status, ConnectionCallback.noop);
|
||||
IOUtils.write(content, out);
|
||||
out.close();
|
||||
file.attributes().setVersionId(((B2FileResponse) out.getStatus()).getFileId());
|
||||
}
|
||||
// Versioned listing: current version + previous duplicate visible
|
||||
{
|
||||
final AttributedList<Path> list = new B2ObjectListService(session, fileid, 10, new VersioningConfiguration(true)).list(folder, new DisabledListProgressListener());
|
||||
assertEquals(2, list.size());
|
||||
assertTrue(list.contains(file));
|
||||
assertNull(list.find(new SimplePathPredicate(file)).attributes().getRevision());
|
||||
assertEquals(Long.valueOf(1L), list.find(path -> path.attributes().isDuplicate()).attributes().getRevision());
|
||||
}
|
||||
// Delete file: add hide marker by nullifying version
|
||||
new B2DeleteFeature(session, fileid).delete(
|
||||
Collections.singletonList(new Path(file).withAttributes(new DefaultPathAttributes(file.attributes()).setVersionId(null))),
|
||||
LoginCallback.noop, new Delete.DisabledCallback());
|
||||
// Delete folder placeholder
|
||||
new B2DeleteFeature(session, fileid).delete(
|
||||
Collections.singletonList(folder), LoginCallback.noop, new Delete.DisabledCallback());
|
||||
// Versioned listing: all file versions are trashed or duplicate (hide marker present, no live entries)
|
||||
final AttributedList<Path> versions = new B2ObjectListService(session, fileid, 10, new VersioningConfiguration(true)).list(folder, new DisabledListProgressListener());
|
||||
assertFalse(versions.isEmpty());
|
||||
for(Path f : versions) {
|
||||
assertTrue(f.attributes().isTrashed() || f.attributes().isDuplicate());
|
||||
}
|
||||
// Versioned bucket listing: folder appears as trashed (no live content beneath prefix)
|
||||
{
|
||||
final AttributedList<Path> bucketVersioned = new B2ObjectListService(session, fileid, 10, new VersioningConfiguration(true)).list(bucket, new DisabledListProgressListener());
|
||||
assertNotNull(bucketVersioned.find(new SimplePathPredicate(folder)));
|
||||
assertTrue(bucketVersioned.find(new SimplePathPredicate(folder)).attributes().isTrashed());
|
||||
}
|
||||
// Non-versioned listing: folder not accessible, file not accessible
|
||||
assertTrue(new B2ObjectListService(session, fileid, 10, VersioningConfiguration.empty()).list(bucket, new DisabledListProgressListener()).isEmpty());
|
||||
assertThrows(NotfoundException.class, () -> new B2ObjectListService(session, fileid, 10, VersioningConfiguration.empty()).list(folder, new DisabledListProgressListener()));
|
||||
// Cleanup
|
||||
for(Path f : versions) {
|
||||
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(f), LoginCallback.noop, new Delete.DisabledCallback());
|
||||
}
|
||||
new B2DeleteFeature(session, fileid).delete(Collections.singletonList(bucket), LoginCallback.noop, new Delete.DisabledCallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListLexicographicSortOrderAssumption() throws Exception {
|
||||
final B2VersionIdProvider fileid = new B2VersionIdProvider(session);
|
||||
|
||||
@@ -19,7 +19,6 @@ import ch.cyberduck.core.ConnectionCallback;
|
||||
import ch.cyberduck.core.Path;
|
||||
import ch.cyberduck.core.exception.BackgroundException;
|
||||
import ch.cyberduck.core.features.Read;
|
||||
import ch.cyberduck.core.features.VersionIdProvider;
|
||||
import ch.cyberduck.core.preferences.HostPreferencesFactory;
|
||||
import ch.cyberduck.core.transfer.TransferStatus;
|
||||
|
||||
@@ -33,12 +32,10 @@ public class CteraDelegatingReadFeature implements Read {
|
||||
private static final Logger log = LogManager.getLogger(CteraDelegatingReadFeature.class);
|
||||
|
||||
private final CteraSession session;
|
||||
private final VersionIdProvider versionid;
|
||||
private final boolean directio;
|
||||
|
||||
public CteraDelegatingReadFeature(final CteraSession session, final VersionIdProvider versionid) {
|
||||
public CteraDelegatingReadFeature(final CteraSession session) {
|
||||
this.session = session;
|
||||
this.versionid = versionid;
|
||||
this.directio = HostPreferencesFactory.get(session.getHost()).getBoolean("ctera.download.directio.enable");
|
||||
}
|
||||
|
||||
|
||||
@@ -277,7 +277,7 @@ public class CteraSession extends DAVSession {
|
||||
return (T) new CteraListService(this);
|
||||
}
|
||||
if(type == Read.class) {
|
||||
return (T) new CteraDelegatingReadFeature(this, versionid);
|
||||
return (T) new CteraDelegatingReadFeature(this);
|
||||
}
|
||||
if(type == Write.class) {
|
||||
return (T) new CteraWriteFeature(this);
|
||||
|
||||
@@ -84,12 +84,12 @@ N.B. no need to check `readpermission` upon mv/cp.
|
||||
|
||||
| ACL (CTERA) | POSIX (Folder) | POSIX (File) | Windows `FileSystemRights` (Folder) | Windows `FileSystemRights` (File) | Example (Folder) | Example (File) |
|
||||
|----------------------------------------------------------------------------------------|-----------------------------------------------------|--------------------------------------|-----------------------------------------------------------|-----------------------------------|-----------------------------------------------------------------|--------------------------------------------------------------|
|
||||
| - | `---` | - | empty | - | `/ACL test (new user)/NoAccess/` | - |
|
||||
| `readpermission` | `r-x` | `r--` | `ReadAndExecute` | `Read` | `/ACL test (new user)/ReadOnly/` | `/ACL test (new user)/ReadOnly/ReadOnly.txt` |
|
||||
| `readpermission`, `createdirectoriespermission` | `rwx` (delete prevented in preflight) | - | `ReadAndExecute`, `CreateDirectories`, `CreateFiles` (!), | - | `/WORM test (new user)/Retention Folder (no write, no delete)/` | - |
|
||||
| `readpermission`, `deletepermission` | `rwx` (folder/file creation prevented in preflight) | `rw-` (write prevented in preflight) | `ReadAndExecute`, `Delete` | `Read`, `Delete` | `/ACL test (new user)/NoCreateFolderPermission` | `/ACL test (new user)/NoCreateFolderPermission/trayIcon.png` |
|
||||
| `readpermission`, `deletepermission`, `writepermission` | - | `rwx` | - | `Read`, `Delete`, `Write` | - | `/ACL test (new user)/ReadWrite/Free Access.txt` |
|
||||
| `readpermission`, `deletepermission`, `writepermission`, `createdirectoriespermission` | `rwx` | - | `ReadAndExecute`, `Delete`, `Write` | - | `/ACL test (new user)/ReadWrite/` | - |
|
||||
| - | `---` | - | empty | - | `/ACL test/NoAccess/` | - |
|
||||
| `readpermission` | `r-x` | `r--` | `ReadAndExecute` | `Read` | `/ACL test/ReadOnly/` | `/ACL test/ReadOnly/ReadOnly.txt` |
|
||||
| `readpermission`, `createdirectoriespermission` | `rwx` (delete prevented in preflight) | - | `ReadAndExecute`, `CreateDirectories`, `CreateFiles` (!), | - | `/WORM test/Retention Folder (no write, no delete)/` | - |
|
||||
| `readpermission`, `deletepermission` | `rwx` (folder/file creation prevented in preflight) | `rw-` (write prevented in preflight) | `ReadAndExecute`, `Delete` | `Read`, `Delete` | `/ACL test/NoCreateFolderPermission` | `/ACL test/NoCreateFolderPermission/trayIcon.png` |
|
||||
| `readpermission`, `deletepermission`, `writepermission` | - | `rwx` | - | `Read`, `Delete`, `Write` | - | `/ACL test/ReadWrite/Free Access.txt` |
|
||||
| `readpermission`, `deletepermission`, `writepermission`, `createdirectoriespermission` | `rwx` | - | `ReadAndExecute`, `Delete`, `Write` | - | `/ACL test/ReadWrite/` | - |
|
||||
|
||||
#### References
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package ch.cyberduck.core.ctera;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002-2022 iterate GmbH. All rights reserved.
|
||||
* https://cyberduck.io/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
import ch.cyberduck.core.Credentials;
|
||||
import ch.cyberduck.core.Host;
|
||||
import ch.cyberduck.core.HostKeyCallback;
|
||||
import ch.cyberduck.core.LoginCallback;
|
||||
import ch.cyberduck.core.LoginConnectionService;
|
||||
import ch.cyberduck.core.ProgressListener;
|
||||
import ch.cyberduck.core.proxy.DisabledProxyFinder;
|
||||
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
|
||||
import ch.cyberduck.core.ssl.DisabledX509TrustManager;
|
||||
import ch.cyberduck.core.threading.CancelCallback;
|
||||
import ch.cyberduck.test.VaultTest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
public class AbstractCteraDirectIOTest extends VaultTest {
|
||||
|
||||
protected CteraSession session;
|
||||
private TestPasswordStore keychain;
|
||||
|
||||
@After
|
||||
public void disconnect() throws Exception {
|
||||
session.close();
|
||||
keychain.save(session.getHost());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
final Host host = new Host(new CteraProtocol(), "dcdirect.ctera.me", new Credentials(PROPERTIES.get("ctera.directio.user"))) {
|
||||
@Override
|
||||
public String getProperty(final String key) {
|
||||
if("ctera.download.directio.enable".equals(key)) {
|
||||
return String.valueOf(true);
|
||||
}
|
||||
return super.getProperty(key);
|
||||
}
|
||||
};
|
||||
host.setDefaultPath("/ServicesPortal/webdav/My Files");
|
||||
keychain = new TestPasswordStore();
|
||||
session = new CteraSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager(), keychain);
|
||||
final LoginConnectionService connect = new LoginConnectionService(LoginCallback.noop, HostKeyCallback.noop,
|
||||
keychain, ProgressListener.noop, new DisabledProxyFinder());
|
||||
connect.check(session, CancelCallback.noop);
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,15 @@ public class AbstractCteraTest extends VaultTest {
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
final Host host = new Host(new CteraProtocol(), "driveconnect.ctera.me", new Credentials(PROPERTIES.get("ctera.user")));
|
||||
final Host host = new Host(new CteraProtocol(), PROPERTIES.get("ctera.hostname"), new Credentials(PROPERTIES.get("ctera.user"))) {
|
||||
@Override
|
||||
public String getProperty(final String key) {
|
||||
if("ctera.download.directio.enable".equals(key)) {
|
||||
return String.valueOf(true);
|
||||
}
|
||||
return super.getProperty(key);
|
||||
}
|
||||
};
|
||||
host.setDefaultPath("/ServicesPortal/webdav/My Files");
|
||||
session = new CteraSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
|
||||
final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() {
|
||||
|
||||
@@ -108,7 +108,7 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testNoAccessAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test", EnumSet.of(AbstractPath.Type.directory));
|
||||
|
||||
// list parent folder to inspect attributes
|
||||
final List<DavResource> noAccess = new CteraListService(session).propfind(home).stream().filter(r -> r.getName().equals("NoAccess")).collect(Collectors.toList());
|
||||
@@ -119,8 +119,8 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
assertEquals("false", resource.getCustomProps().get(READPERMISSION.getName()));
|
||||
assertEquals("false", resource.getCustomProps().get(DELETEPERMISSION.getName()));
|
||||
assertEquals("false", resource.getCustomProps().get(CREATEDIRECTORIESPERMISSION.getName()));
|
||||
assertEquals("bb64b3a4-399e-45d0-95af-43f1ace6e250:105620641", resource.getCustomProps().get(CTERA_GUID));
|
||||
assertEquals("105620644", resource.getCustomProps().get(CTERA_FILEID));
|
||||
assertEquals("05c75d64-bb1e-4be8-8ec3-19370247dfec:913", resource.getCustomProps().get(CTERA_GUID));
|
||||
assertEquals("47836", resource.getCustomProps().get(CTERA_FILEID));
|
||||
assertEquals(new Acl(new Acl.CanonicalUser()), new CteraAttributesFinderFeature(session).toAttributes(resource).getAcl());
|
||||
// find fails with 403 in backend
|
||||
final AccessDeniedException findException = assertThrows(AccessDeniedException.class, () -> new CteraAttributesFinderFeature(session).find(new Path(home, "NoAccess", EnumSet.of(AbstractPath.Type.directory))));
|
||||
@@ -133,10 +133,12 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testNoDeleteAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "NoDelete", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl folderAcl = new CteraAttributesFinderFeature(session).find(folder).getAcl();
|
||||
assertEquals(new Acl(new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION)), folderAcl);
|
||||
assertEquals(new Acl(new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), WRITEPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), CREATEDIRECTORIESPERMISSION)), folderAcl);
|
||||
|
||||
final Path file = new Path(folder, "RW no delete.txt", EnumSet.of(AbstractPath.Type.file));
|
||||
final Acl fileAcl = new CteraAttributesFinderFeature(session).find(file).getAcl();
|
||||
@@ -145,7 +147,7 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testReadOnlyAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "ReadOnly", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl folderAcl = new CteraAttributesFinderFeature(session).find(folder).getAcl();
|
||||
assertEquals(new Acl(new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION)), folderAcl);
|
||||
@@ -157,7 +159,7 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testNoCreateFolderAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "NoCreateFolderPermission", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl folderAcl = new CteraAttributesFinderFeature(session).find(folder).getAcl();
|
||||
assertEquals(new Acl(
|
||||
@@ -176,7 +178,7 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testReadWriteAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me/ACL test", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "ReadWrite", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl folderAcl = new CteraAttributesFinderFeature(session).find(folder).getAcl();
|
||||
assertEquals(new Acl(
|
||||
@@ -206,15 +208,7 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
@Test
|
||||
public void testWORMAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "WORM test (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl folderAcl = new CteraAttributesFinderFeature(session).find(folder).getAcl();
|
||||
assertEquals(new Acl(
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), WRITEPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), DELETEPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), CREATEDIRECTORIESPERMISSION)
|
||||
), folderAcl);
|
||||
|
||||
final Path folder = new Path(home, "WORM test", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path subfolder = new Path(folder, "Retention Folder (no write, no delete)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl subfolderAcl = new CteraAttributesFinderFeature(session).find(subfolder).getAcl();
|
||||
assertEquals(new Acl(
|
||||
@@ -227,26 +221,14 @@ public class CteraAttributesFinderFeatureTest extends AbstractCteraTest {
|
||||
assertEquals(new Acl(
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION)
|
||||
), fileAcl);
|
||||
|
||||
final Path emptySubfolder = new Path(folder, "Empty WORM folder", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl emptySubfolderAcl = new CteraAttributesFinderFeature(session).find(emptySubfolder).getAcl();
|
||||
assertEquals(new Acl(
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), CREATEDIRECTORIESPERMISSION)
|
||||
), emptySubfolderAcl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWORMNoRetentionAcl() throws Exception {
|
||||
final Path home = new Path("/ServicesPortal/webdav/Shared With Me", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "WORM-NoRetention(Delete allowed) (new user)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Path folder = new Path(home, "WORM-NoRetention(Delete allowed)", EnumSet.of(AbstractPath.Type.directory));
|
||||
final Acl folderAcl = new CteraAttributesFinderFeature(session).find(folder).getAcl();
|
||||
assertEquals(new Acl(
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), READPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), WRITEPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), DELETEPERMISSION),
|
||||
new Acl.UserAndRole(new Acl.CanonicalUser(), CREATEDIRECTORIESPERMISSION)
|
||||
), folderAcl);
|
||||
assertEquals(Acl.EMPTY, folderAcl);
|
||||
|
||||
final Path file = new Path(folder, "WORM-DeleteAllowed.txt", EnumSet.of(AbstractPath.Type.file));
|
||||
final Acl fileAcle = new CteraAttributesFinderFeature(session).find(file).getAcl();
|
||||
|
||||
@@ -58,7 +58,7 @@ import java.util.EnumSet;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@Category(IntegrationTest.class)
|
||||
public class CteraConcurrentTransferWorkerTest extends AbstractCteraDirectIOTest {
|
||||
public class CteraConcurrentTransferWorkerTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testBelowSegmentSizeUpAndDownload() throws Exception {
|
||||
|
||||
@@ -5,11 +5,8 @@ import ch.cyberduck.core.AlphanumericRandomStringService;
|
||||
import ch.cyberduck.core.LoginCallback;
|
||||
import ch.cyberduck.core.Path;
|
||||
import ch.cyberduck.core.dav.DAVFindFeature;
|
||||
import ch.cyberduck.core.dav.DAVLockFeature;
|
||||
import ch.cyberduck.core.exception.AccessDeniedException;
|
||||
import ch.cyberduck.core.exception.InteroperabilityException;
|
||||
import ch.cyberduck.core.exception.NotfoundException;
|
||||
import ch.cyberduck.core.exception.RetriableAccessDeniedException;
|
||||
import ch.cyberduck.core.features.Delete;
|
||||
import ch.cyberduck.core.shared.DefaultHomeFinderService;
|
||||
import ch.cyberduck.core.transfer.TransferStatus;
|
||||
@@ -37,22 +34,6 @@ public class CteraDeleteFeatureTest extends AbstractCteraTest {
|
||||
assertFalse(new DAVFindFeature(session).find(test));
|
||||
}
|
||||
|
||||
@Test(expected = RetriableAccessDeniedException.class)
|
||||
public void testDeleteFileWithLock() throws Exception {
|
||||
final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file));
|
||||
new CteraTouchFeature(session).touch(new CteraWriteFeature(session), test, new TransferStatus());
|
||||
String lock = null;
|
||||
try {
|
||||
lock = new DAVLockFeature(session).lock(test);
|
||||
}
|
||||
catch(InteroperabilityException e) {
|
||||
// Not supported
|
||||
}
|
||||
assertTrue(new DAVFindFeature(session).find(test));
|
||||
new CteraDeleteFeature(session).delete(Collections.singletonMap(test, new TransferStatus().setLockId(lock)), LoginCallback.noop, new Delete.DisabledCallback());
|
||||
assertFalse(new DAVFindFeature(session).find(test));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteDirectory() throws Exception {
|
||||
final Path test = new Path(new DefaultHomeFinderService(session).find(), new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory));
|
||||
|
||||
@@ -50,7 +50,7 @@ import java.util.EnumSet;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
@Category(IntegrationTest.class)
|
||||
public class CteraDirectIOReadFeatureTest extends AbstractCteraDirectIOTest {
|
||||
public class CteraDirectIOReadFeatureTest extends AbstractCteraTest {
|
||||
|
||||
@Test
|
||||
public void testReadSingleChunk() throws Exception {
|
||||
|
||||
@@ -449,6 +449,7 @@ googledrive.delete.multiple.partition=50
|
||||
|
||||
b2.bucket.acl.default=allPrivate
|
||||
b2.listing.chunksize=1000
|
||||
b2.listing.concurrency=25
|
||||
b2.listing.versioning.enable=true
|
||||
b2.upload.checksum.verify=true
|
||||
b2.upload.largeobject.auto=true
|
||||
|
||||
@@ -325,7 +325,7 @@
|
||||
<dependency>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations</artifactId>
|
||||
<version>2.2.49</version>
|
||||
<version>2.2.50</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.jersey.core</groupId>
|
||||
@@ -741,7 +741,7 @@
|
||||
<plugin>
|
||||
<groupId>io.swagger.codegen.v3</groupId>
|
||||
<artifactId>swagger-codegen-maven-plugin</artifactId>
|
||||
<version>3.0.79</version>
|
||||
<version>3.0.80</version>
|
||||
<configuration>
|
||||
<configOptions>
|
||||
<sourceFolder>src/main/java</sourceFolder>
|
||||
|
||||
@@ -85,12 +85,6 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.cryptomator</groupId>
|
||||
<artifactId>cryptofs</artifactId>
|
||||
<version>2.9.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.sshd</groupId>
|
||||
<artifactId>sshd-sftp</artifactId>
|
||||
|
||||
-240
@@ -1,240 +0,0 @@
|
||||
package ch.cyberduck.core.cryptomator;
|
||||
|
||||
/*
|
||||
* Copyright (c) 2002-2017 iterate GmbH. All rights reserved.
|
||||
* https://cyberduck.io/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
import ch.cyberduck.core.AlphanumericRandomStringService;
|
||||
import ch.cyberduck.core.ConnectionCallback;
|
||||
import ch.cyberduck.core.Credentials;
|
||||
import ch.cyberduck.core.Host;
|
||||
import ch.cyberduck.core.HostKeyCallback;
|
||||
import ch.cyberduck.core.LoginCallback;
|
||||
import ch.cyberduck.core.PasswordCallback;
|
||||
import ch.cyberduck.core.Path;
|
||||
import ch.cyberduck.core.cryptomator.features.CryptoReadFeature;
|
||||
import ch.cyberduck.core.cryptomator.impl.v8.CryptomatorVault;
|
||||
import ch.cyberduck.core.cryptomator.impl.v8.MasterkeyVaultMetadataProvider;
|
||||
import ch.cyberduck.core.proxy.DisabledProxyFinder;
|
||||
import ch.cyberduck.core.sftp.SFTPHomeDirectoryService;
|
||||
import ch.cyberduck.core.sftp.SFTPProtocol;
|
||||
import ch.cyberduck.core.sftp.SFTPReadFeature;
|
||||
import ch.cyberduck.core.sftp.SFTPSession;
|
||||
import ch.cyberduck.core.ssl.DefaultX509KeyManager;
|
||||
import ch.cyberduck.core.ssl.DisabledX509TrustManager;
|
||||
import ch.cyberduck.core.threading.CancelCallback;
|
||||
import ch.cyberduck.core.transfer.TransferStatus;
|
||||
import ch.cyberduck.core.vault.DefaultVaultRegistry;
|
||||
import ch.cyberduck.core.vault.VaultCredentials;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.RandomUtils;
|
||||
import org.apache.commons.text.RandomStringGenerator;
|
||||
import org.apache.sshd.common.file.virtualfs.VirtualFileSystemFactory;
|
||||
import org.apache.sshd.server.SshServer;
|
||||
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
|
||||
import org.apache.sshd.sftp.server.SftpSubsystemFactory;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
public class SFTPCryptomatorInteroperabilityTest {
|
||||
|
||||
private final int PORT_NUMBER = ThreadLocalRandom.current().nextInt(2000, 3000);
|
||||
|
||||
private static SshServer server;
|
||||
private CryptoFileSystem cryptoFileSystem;
|
||||
private java.nio.file.Path tempDir;
|
||||
private String passphrase;
|
||||
|
||||
@Before
|
||||
public void startSerer() throws Exception {
|
||||
server = SshServer.setUpDefaultServer();
|
||||
server.setPort(PORT_NUMBER);
|
||||
server.setPasswordAuthenticator((username, password, session) -> true);
|
||||
server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider());
|
||||
server.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
|
||||
tempDir = Files.createTempDirectory(String.format("%s-", this.getClass().getName()));
|
||||
|
||||
unzipTestVault("/testvault.zip", tempDir.toString());
|
||||
|
||||
//TODO switch back to cryptofs based testing as soon as cryptofs is based on new cryptolib
|
||||
/*
|
||||
final java.nio.file.Path vault = tempDir.resolve("vault");
|
||||
Files.createDirectory(vault);
|
||||
passphrase = new AlphanumericRandomStringService().random();
|
||||
final SecureRandom csprng;
|
||||
switch(Factory.Platform.getDefault()) {
|
||||
case windows:
|
||||
csprng = ReseedingSecureRandom.create(SecureRandom.getInstanceStrong());
|
||||
break;
|
||||
default:
|
||||
csprng = FastSecureRandomProvider.get().provide();
|
||||
}
|
||||
final PerpetualMasterkey mk = Masterkey.generate(csprng);
|
||||
final MasterkeyFileAccess mkAccess = new MasterkeyFileAccess(PreferencesFactory.get().getProperty("cryptomator.vault.pepper").getBytes(StandardCharsets.UTF_8), csprng);
|
||||
final java.nio.file.Path mkPath = Paths.get(vault.toString(), DefaultVaultRegistry.DEFAULT_MASTERKEY_FILE_NAME);
|
||||
mkAccess.persist(mk, mkPath, passphrase);
|
||||
CryptoFileSystemProperties properties = cryptoFileSystemProperties().withKeyLoader(new MasterkeyLoader() {
|
||||
@Override
|
||||
public Masterkey loadKey(final URI keyId) throws MasterkeyLoadingFailedException {
|
||||
return mkAccess.load(mkPath, passphrase);
|
||||
}
|
||||
})
|
||||
.withCipherCombo(CryptorProvider.Scheme.SIV_CTRMAC)
|
||||
.build();
|
||||
CryptoFileSystemProvider.initialize(vault, properties, URI.create("test:key"));
|
||||
cryptoFileSystem = CryptoFileSystemProvider.newFileSystem(vault, properties);
|
||||
*/
|
||||
server.setFileSystemFactory(new VirtualFileSystemFactory(tempDir.toAbsolutePath()));
|
||||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stop() throws Exception {
|
||||
server.stop();
|
||||
FileUtils.deleteDirectory(tempDir.toFile());
|
||||
/*
|
||||
cryptoFileSystem.close();
|
||||
FileUtils.deleteDirectory(cryptoFileSystem.getPathToVault().getParent().toFile());
|
||||
*/
|
||||
}
|
||||
|
||||
private void unzipTestVault(final String zip, final String target) throws Exception {
|
||||
try(InputStream is = this.getClass().getResourceAsStream(zip);
|
||||
ZipInputStream zipIn = new ZipInputStream(is)) {
|
||||
|
||||
java.nio.file.Path targetDir = Paths.get(target);
|
||||
Files.createDirectories(targetDir);
|
||||
|
||||
ZipEntry entry;
|
||||
while((entry = zipIn.getNextEntry()) != null) {
|
||||
java.nio.file.Path filePath = targetDir.resolve(entry.getName());
|
||||
System.out.println(filePath.toString());
|
||||
|
||||
if(entry.isDirectory()) {
|
||||
Files.createDirectories(filePath);
|
||||
}
|
||||
else {
|
||||
Files.createDirectories(filePath.getParent());
|
||||
Files.copy(zipIn, filePath, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
zipIn.closeEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create file/folder with Cryptomator, read with Cyberduck
|
||||
*/
|
||||
@Ignore(value = "Need a Cryptofs version that is based on the new Cryptolib")
|
||||
@Test(expected = CryptoInvalidFilenameException.class)
|
||||
public void testCryptomatorInteroperabilityLongFilename() throws Exception {
|
||||
// create folder
|
||||
final java.nio.file.Path targetFolder = cryptoFileSystem.getPath("/", new AlphanumericRandomStringService().random());
|
||||
Files.createDirectory(targetFolder);
|
||||
// create file and write some random content
|
||||
java.nio.file.Path targetFile = targetFolder.resolve(new RandomStringGenerator.Builder().build().generate(220));
|
||||
final byte[] content = RandomUtils.nextBytes(48768);
|
||||
Files.write(targetFile, content);
|
||||
|
||||
// read with Cyberduck and compare
|
||||
final Host host = new Host(new SFTPProtocol(), "localhost", PORT_NUMBER, new Credentials("empty", "empty"));
|
||||
final SFTPSession session = new SFTPSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
|
||||
session.open(new DisabledProxyFinder(), HostKeyCallback.noop, LoginCallback.noop, CancelCallback.noop);
|
||||
session.login(LoginCallback.noop, CancelCallback.noop);
|
||||
final Path home = new SFTPHomeDirectoryService(session).find();
|
||||
final Path vaultPath = new Path(home, "vault", EnumSet.of(Path.Type.directory));
|
||||
final AbstractVault cryptomator = new CryptomatorVault(vaultPath);
|
||||
cryptomator.load(session, new MasterkeyVaultMetadataProvider(new VaultCredentials("12341234")));
|
||||
session.withRegistry(new DefaultVaultRegistry(PasswordCallback.noop, cryptomator));
|
||||
Path p = new Path(new Path(vaultPath, targetFolder.getFileName().toString(), EnumSet.of(Path.Type.directory)), targetFile.getFileName().toString(), EnumSet.of(Path.Type.file));
|
||||
final InputStream read = new CryptoReadFeature(session, new SFTPReadFeature(session), cryptomator).read(p, new TransferStatus(), ConnectionCallback.noop);
|
||||
final byte[] readContent = new byte[content.length];
|
||||
IOUtils.readFully(read, readContent);
|
||||
assertArrayEquals(content, readContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Cryptomator generated vault with long file and folder names
|
||||
*/
|
||||
@Test
|
||||
public void testCryptomatorInteroperabilityLongFileAndFoldername() throws Exception {
|
||||
|
||||
|
||||
// read with Cyberduck and compare
|
||||
final Host host = new Host(new SFTPProtocol(), "localhost", PORT_NUMBER, new Credentials("empty", "empty"));
|
||||
final SFTPSession session = new SFTPSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
|
||||
session.open(new DisabledProxyFinder(), HostKeyCallback.noop, LoginCallback.noop, CancelCallback.noop);
|
||||
session.login(LoginCallback.noop, CancelCallback.noop);
|
||||
final Path vaultPath = new SFTPHomeDirectoryService(session).find();
|
||||
final AbstractVault cryptomator = new CryptomatorVault(vaultPath);
|
||||
cryptomator.load(session, new MasterkeyVaultMetadataProvider(new VaultCredentials("12341234")));
|
||||
session.withRegistry(new DefaultVaultRegistry(PasswordCallback.noop, cryptomator));
|
||||
|
||||
/*
|
||||
Path p = new Path(new Path(vaultPath, targetFolder.getFileName().toString(), EnumSet.of(Path.Type.directory)), targetFile.getFileName().toString(), EnumSet.of(Path.Type.file));
|
||||
final InputStream read = new CryptoReadFeature(session, new SFTPReadFeature(session), cryptomator).read(p, new TransferStatus(), ConnectionCallback.noop);
|
||||
final byte[] readContent = new byte[content.length];
|
||||
IOUtils.readFully(read, readContent);
|
||||
assertArrayEquals(content, readContent);*/
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create long file/folder with Cryptomator, read with Cyberduck
|
||||
*/
|
||||
@Ignore(value = "Need a Cryptofs version that is based on the new Cryptolib")
|
||||
@Test
|
||||
public void testCryptomatorInteroperability() throws Exception {
|
||||
// create folder
|
||||
final java.nio.file.Path targetFolder = cryptoFileSystem.getPath("/", new AlphanumericRandomStringService().random());
|
||||
Files.createDirectory(targetFolder);
|
||||
// create file and write some random content
|
||||
java.nio.file.Path targetFile = targetFolder.resolve(new AlphanumericRandomStringService().random());
|
||||
final byte[] content = RandomUtils.nextBytes(20);
|
||||
Files.write(targetFile, content);
|
||||
|
||||
// read with Cyberduck and compare
|
||||
final Host host = new Host(new SFTPProtocol(), "localhost", PORT_NUMBER, new Credentials("empty", "empty"));
|
||||
final SFTPSession session = new SFTPSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager());
|
||||
session.open(new DisabledProxyFinder(), HostKeyCallback.noop, LoginCallback.noop, CancelCallback.noop);
|
||||
session.login(LoginCallback.noop, CancelCallback.noop);
|
||||
final Path home = new SFTPHomeDirectoryService(session).find();
|
||||
final Path vaultPath = new Path(home, "vault", EnumSet.of(Path.Type.directory));
|
||||
final AbstractVault cryptomator = new CryptomatorVault(vaultPath);
|
||||
cryptomator.load(session, new MasterkeyVaultMetadataProvider(new VaultCredentials("12341234")));
|
||||
session.withRegistry(new DefaultVaultRegistry(PasswordCallback.noop, cryptomator));
|
||||
Path p = new Path(new Path(vaultPath, targetFolder.getFileName().toString(), EnumSet.of(Path.Type.directory)), targetFile.getFileName().toString(), EnumSet.of(Path.Type.file));
|
||||
final InputStream read = new CryptoReadFeature(session, new SFTPReadFeature(session), cryptomator).read(p, new TransferStatus(), ConnectionCallback.noop);
|
||||
final byte[] readContent = new byte[content.length];
|
||||
IOUtils.readFully(read, readContent);
|
||||
assertArrayEquals(content, readContent);
|
||||
}
|
||||
}
|
||||
@@ -134,6 +134,8 @@
|
||||
<li><span class="label label-warning">Bugfix</span> Support "Include" directive when reading from OpenSSH config
|
||||
(SFTP) (<a target="_blank" href="https://trac.cyberduck.io/ticket/10451">#10451</a>)
|
||||
</li>
|
||||
<li><span class="label label-warning">Bugfix</span> Exclude trashed folders in list by default (Backblaze B2) (<a
|
||||
target="_blank" href="https://trac.cyberduck.io/ticket/#18101">##18101</a></li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
|
||||
Reference in New Issue
Block a user