From bc9f1324b61b9bca93d5b234ec026e9057e5d63f Mon Sep 17 00:00:00 2001 From: David Kocher Date: Tue, 15 Jul 2025 12:19:11 +0200 Subject: [PATCH 1/3] Rewrite Azure implementation. This reverts commit c52d0be8 --- azure/pom.xml | 6 +- .../core/azure/AzureAclPermissionFeature.java | 79 +++--- .../azure/AzureAttributesFinderFeature.java | 91 ++++--- .../core/azure/AzureContainerListService.java | 49 ++-- .../core/azure/AzureCopyFeature.java | 57 ++-- .../core/azure/AzureDeleteFeature.java | 47 +--- .../core/azure/AzureDirectoryFeature.java | 40 +-- .../azure/AzureExceptionMappingService.java | 43 ++- .../core/azure/AzureFindFeature.java | 70 +---- .../core/azure/AzureListService.java | 12 +- .../core/azure/AzureLoggingFeature.java | 52 ++-- .../core/azure/AzureMetadataFeature.java | 79 ++---- .../core/azure/AzureMoveFeature.java | 17 +- .../core/azure/AzureObjectListService.java | 95 +++---- .../cyberduck/core/azure/AzureProtocol.java | 21 +- .../core/azure/AzureReadFeature.java | 79 ++---- .../ch/cyberduck/core/azure/AzureSession.java | 246 ++++++++---------- .../core/azure/AzureTouchFeature.java | 18 +- .../core/azure/AzureUploadFeature.java | 11 +- .../core/azure/AzureUrlProvider.java | 102 ++++---- .../core/azure/AzureWriteFeature.java | 211 ++++++++------- .../core/azure/apache/ApacheHttpClient.java | 80 ++++++ .../core/azure/apache/ApacheHttpResponse.java | 79 ++++++ .../core/azure/AbstractAzureTest.java | 22 +- .../azure/AzureAclPermissionFeatureTest.java | 22 +- .../AzureAttributesFinderFeatureTest.java | 44 ++-- .../core/azure/AzureCopyFeatureTest.java | 35 +-- .../core/azure/AzureDeleteFeatureTest.java | 22 +- .../core/azure/AzureDirectoryFeatureTest.java | 34 +-- .../core/azure/AzureFindFeatureTest.java | 47 ++-- .../core/azure/AzureLoggingFeatureTest.java | 2 +- .../core/azure/AzureMetadataFeatureTest.java | 18 +- .../core/azure/AzureMoveFeatureTest.java | 25 +- .../azure/AzureObjectListServiceTest.java | 37 +-- .../core/azure/AzureReadFeatureTest.java | 20 +- .../core/azure/AzureSessionTest.java | 65 ++++- .../core/azure/AzureTouchFeatureTest.java | 6 +- .../core/azure/AzureUrlProviderTest.java | 33 ++- .../core/azure/AzureWriteFeatureTest.java | 49 ++-- .../AzureDirectoryFeatureTest.java | 10 +- .../cryptomator/AzureListServiceTest.java | 51 +--- .../cryptomator/AzureMoveFeatureTest.java | 18 +- .../cryptomator/AzureTouchFeatureTest.java | 10 +- .../cryptomator/AzureWriteFeatureTest.java | 13 +- .../core/cryptomator/CopyWorkerTest.java | 80 +++--- .../CryptoAzureSingleTransferWorkerTest.java | 23 +- .../worker/AzureSingleTransferWorkerTest.java | 13 +- .../cyberduck/core/worker/CopyWorkerTest.java | 36 +-- 48 files changed, 1122 insertions(+), 1197 deletions(-) create mode 100644 azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java create mode 100644 azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java diff --git a/azure/pom.xml b/azure/pom.xml index cd018873dc3..ac3a23dfcba 100644 --- a/azure/pom.xml +++ b/azure/pom.xml @@ -22,7 +22,7 @@ azure - 8.6.6 + 12.26.1 @@ -39,8 +39,8 @@ ${project.version} - com.microsoft.azure - azure-storage + com.azure + azure-storage-blob ${azure-storage-version} diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java index d75ef6e1e8e..7ab1a2352eb 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureAclPermissionFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.Acl; @@ -23,49 +20,42 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.AclPermission; import ch.cyberduck.core.transfer.TransferStatus; import org.jets3t.service.acl.Permission; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobContainerPermissions; -import com.microsoft.azure.storage.blob.BlobContainerPublicAccessType; -import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.models.BlobContainerAccessPolicies; +import com.azure.storage.blob.models.PublicAccessType; /** - * By default, a container and any blobs within it may be accessed only by the owner of the storage account. - * If you want to give anonymous users read permissions to a container and its blobs, you can set the container - * permissions to allow public access. Anonymous users can read blobs within a publicly accessible container without authenticating the request. - * Containers provide the following options for managing container access: + * By default, a container and any blobs within it may be accessed only by the owner of the storage account. If you want + * to give anonymous users read permissions to a container and its blobs, you can set the container permissions to allow + * public access. Anonymous users can read blobs within a publicly accessible container without authenticating the + * request. Containers provide the following options for managing container access: *

- * Full public read access: Container and blob data can be read via anonymous request. Clients can enumerate - * blobs within the container via anonymous request, but cannot enumerate containers within the storage account. + * Full public read access: Container and blob data can be read via anonymous request. Clients can enumerate blobs + * within the container via anonymous request, but cannot enumerate containers within the storage account. *

- * Public read access for blobs only: Blob data within this container can be read via anonymous request, but - * container data is not available. Clients cannot enumerate blobs within the container via anonymous request. + * Public read access for blobs only: Blob data within this container can be read via anonymous request, but container + * data is not available. Clients cannot enumerate blobs within the container via anonymous request. *

* No public read access: Container and blob data can be read by the account owner only. */ public class AzureAclPermissionFeature implements AclPermission { private final AzureSession session; - - private final OperationContext context; - private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - public AzureAclPermissionFeature(final AzureSession session, final OperationContext context) { + public AzureAclPermissionFeature(final AzureSession session) { this.session = session; - this.context = context; } @Override @@ -76,7 +66,7 @@ public List getAvailableAclRoles(final List files) { @Override public List getAvailableAclUsers() { - return new ArrayList(Collections.singletonList( + return new ArrayList<>(Collections.singletonList( new Acl.GroupUser(Acl.GroupUser.EVERYONE, false)) ); } @@ -85,22 +75,21 @@ public List getAvailableAclUsers() { public Acl getPermission(final Path file) throws BackgroundException { try { if(containerService.isContainer(file)) { - final CloudBlobContainer container = session.getClient() - .getContainerReference(containerService.getContainer(file).getName()); - final BlobContainerPermissions permissions = container.downloadPermissions(null, null, context); + final BlobContainerClient client = session.getClient() + .getBlobContainerClient(containerService.getContainer(file).getName()); + final BlobContainerAccessPolicies accessPolicy = client.getAccessPolicy(); final Acl acl = new Acl(); - if(permissions.getPublicAccess().equals(BlobContainerPublicAccessType.BLOB) - || permissions.getPublicAccess().equals(BlobContainerPublicAccessType.CONTAINER)) { - acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE, false), new Acl.Role(Acl.Role.READ)); + if(accessPolicy.getBlobAccessType() != null) { + if(accessPolicy.getBlobAccessType().equals(PublicAccessType.BLOB) + || accessPolicy.getBlobAccessType().equals(PublicAccessType.CONTAINER)) { + acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE, false), new Acl.Role(Acl.Role.READ)); + } } return acl; } return Acl.EMPTY; } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file); } } @@ -109,23 +98,21 @@ public Acl getPermission(final Path file) throws BackgroundException { public void setPermission(final Path file, final TransferStatus status) throws BackgroundException { try { if(containerService.isContainer(file)) { - final CloudBlobContainer container = session.getClient() - .getContainerReference(containerService.getContainer(file).getName()); - final BlobContainerPermissions permissions = container.downloadPermissions(null, null, context); + final BlobContainerClient client = session.getClient() + .getBlobContainerClient(containerService.getContainer(file).getName()); + if(status.getAcl().asList().isEmpty()) { + client.setAccessPolicy(null, Collections.emptyList()); + } for(Acl.UserAndRole userAndRole : status.getAcl().asList()) { if(userAndRole.getUser() instanceof Acl.GroupUser) { if(userAndRole.getUser().getIdentifier().equals(Acl.GroupUser.EVERYONE)) { - permissions.setPublicAccess(BlobContainerPublicAccessType.BLOB); + client.setAccessPolicy(PublicAccessType.BLOB, Collections.emptyList()); } } } - container.uploadPermissions(permissions, null, null, context); } } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Cannot change permissions of {0}", e, file); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java index 1295519de8a..4d87ef5225b 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureAttributesFinderFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.CancellingListProgressListener; @@ -26,41 +23,36 @@ import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.ListCanceledException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.AttributesAdapter; import ch.cyberduck.core.features.AttributesFinder; import ch.cyberduck.core.io.Checksum; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; -import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpStatus; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; -import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; -import com.microsoft.azure.storage.AccessCondition; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobContainerProperties; -import com.microsoft.azure.storage.blob.BlobProperties; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.models.BlobContainerProperties; +import com.azure.storage.blob.models.BlobItemProperties; +import com.azure.storage.blob.models.BlobProperties; -public class AzureAttributesFinderFeature implements AttributesFinder, AttributesAdapter { +public class AzureAttributesFinderFeature implements AttributesFinder, AttributesAdapter { + private static final Logger log = LogManager.getLogger(AzureAttributesFinderFeature.class); private final AzureSession session; - private final OperationContext context; private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); public static final String KEY_BLOB_TYPE = "blob_type"; - public AzureAttributesFinderFeature(final AzureSession session, final OperationContext context) { + public AzureAttributesFinderFeature(final AzureSession session) { this.session = session; - this.context = context; } @Override @@ -71,23 +63,20 @@ public PathAttributes find(final Path file, final ListProgressListener listener) try { if(containerService.isContainer(file)) { final PathAttributes attributes = new PathAttributes(); - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName()); - container.downloadAttributes(null, null, context); - final BlobContainerProperties properties = container.getProperties(); - attributes.setETag(properties.getEtag()); - attributes.setModificationDate(properties.getLastModified().getTime()); + final BlobContainerClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()); + final BlobContainerProperties properties = client.getProperties(); + attributes.setETag(properties.getETag()); + attributes.setModificationDate(properties.getLastModified().toInstant().toEpochMilli()); return attributes; } if(file.isFile() || file.isPlaceholder()) { try { - final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlobReferenceFromServer(containerService.getKey(file)); - final BlobRequestOptions options = new BlobRequestOptions(); - blob.downloadAttributes(AccessCondition.generateEmptyCondition(), options, context); - return this.toAttributes(blob); + final BlobProperties properties = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)).getBlockBlobClient().getProperties(); + return this.toAttributes(properties); } - catch(StorageException e) { - switch(e.getHttpStatusCode()) { + catch(HttpResponseException e) { + switch(e.getResponse().getStatusCode()) { case HttpStatus.SC_NOT_FOUND: if(file.isPlaceholder()) { // Ignore failure and look for common prefix @@ -98,9 +87,11 @@ public PathAttributes find(final Path file, final ListProgressListener listener) } } } - // Check for common prefix + if(log.isDebugEnabled()) { + log.debug(String.format("Search for common prefix %s", file)); + } try { - new AzureObjectListService(session, context).list(file, new CancellingListProgressListener()); + new AzureObjectListService(session).list(file, new CancellingListProgressListener()); return PathAttributes.EMPTY; } catch(ListCanceledException l) { @@ -108,27 +99,33 @@ public PathAttributes find(final Path file, final ListProgressListener listener) return PathAttributes.EMPTY; } } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } } - @Override - public PathAttributes toAttributes(final CloudBlob blob) { + public PathAttributes toAttributes(final BlobProperties properties) { final PathAttributes attributes = new PathAttributes(); - final BlobProperties properties = blob.getProperties(); - attributes.setSize(properties.getLength()); - attributes.setModificationDate(properties.getLastModified().getTime()); - if(StringUtils.isNotBlank(properties.getContentMD5())) { - attributes.setChecksum(Checksum.parse(Hex.encodeHexString(Base64.decodeBase64(properties.getContentMD5())))); + attributes.setSize(properties.getBlobSize()); + attributes.setModificationDate(properties.getLastModified().toInstant().toEpochMilli()); + if(properties.getContentMd5() != null) { + attributes.setChecksum(Checksum.parse(Hex.encodeHexString(Base64.decodeBase64(properties.getContentMd5())))); } - attributes.setETag(properties.getEtag()); + attributes.setETag(properties.getETag()); final Map custom = new HashMap<>(); custom.put(AzureAttributesFinderFeature.KEY_BLOB_TYPE, properties.getBlobType().name()); attributes.setCustom(custom); return attributes; } + + public PathAttributes toAttributes(final BlobItemProperties properties) { + final PathAttributes attributes = new PathAttributes(); + attributes.setSize(properties.getContentLength()); + attributes.setModificationDate(properties.getLastModified().toInstant().toEpochMilli()); + attributes.setETag(properties.getETag()); + if(properties.getContentMd5() != null) { + attributes.setChecksum(Checksum.parse(Hex.encodeHexString(Base64.decodeBase64(properties.getContentMd5())))); + } + return attributes; + } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java index 1e33c4a01b2..f69048afd17 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureContainerListService.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2015 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.AttributedList; @@ -29,49 +26,35 @@ import java.util.EnumSet; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.ResultContinuation; -import com.microsoft.azure.storage.ResultSegment; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlobContainer; -import com.microsoft.azure.storage.blob.ContainerListingDetails; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.models.BlobContainerItem; +import com.azure.storage.blob.models.BlobContainerListDetails; +import com.azure.storage.blob.models.ListBlobContainersOptions; public class AzureContainerListService implements RootListService { private final AzureSession session; - private final OperationContext context; - public AzureContainerListService(final AzureSession session, final OperationContext context) { + public AzureContainerListService(final AzureSession session) { this.session = session; - this.context = context; } @Override public AttributedList list(final Path directory, final ListProgressListener listener) throws BackgroundException { - ResultSegment result; - ResultContinuation token = null; try { final AttributedList containers = new AttributedList<>(); - do { - final BlobRequestOptions options = new BlobRequestOptions(); - result = session.getClient().listContainersSegmented(null, ContainerListingDetails.NONE, - HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"), token, - options, context); - for(CloudBlobContainer container : result.getResults()) { - final PathAttributes attributes = new PathAttributes(); - attributes.setETag(container.getProperties().getEtag()); - attributes.setModificationDate(container.getProperties().getLastModified().getTime()); - containers.add(new Path(PathNormalizer.normalize(container.getName()), - EnumSet.of(Path.Type.volume, Path.Type.directory), attributes)); - } + for(BlobContainerItem container : session.getClient().listBlobContainers(new ListBlobContainersOptions() + .setMaxResultsPerPage(HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize")) + .setDetails(new BlobContainerListDetails().setRetrieveDeleted(false).setRetrieveMetadata(true)), null)) { + final PathAttributes attributes = new PathAttributes(); + attributes.setETag(container.getProperties().getETag()); + attributes.setModificationDate(container.getProperties().getLastModified().toInstant().toEpochMilli()); + containers.add(new Path(PathNormalizer.normalize(container.getName()), EnumSet.of(Path.Type.volume, Path.Type.directory), attributes)); listener.chunk(directory, containers); - token = result.getContinuationToken(); } - while(result.getHasMoreResults()); return containers; } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Listing directory {0} failed", e, directory); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java index e7891df1c5a..f0598d31407 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureCopyFeature.java @@ -1,87 +1,74 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.ConnectionCallback; import ch.cyberduck.core.DirectoryDelimiterPathContainerService; import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Copy; import ch.cyberduck.core.io.StreamListener; -import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.net.URI; -import java.net.URISyntaxException; import java.text.MessageFormat; +import java.time.Duration; import java.util.Optional; -import com.microsoft.azure.storage.AccessCondition; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlob; +import com.azure.core.exception.HttpResponseException; +import com.azure.core.util.polling.SyncPoller; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.models.BlobCopyInfo; +import com.azure.storage.blob.options.BlobBeginCopyOptions; public class AzureCopyFeature implements Copy { private static final Logger log = LogManager.getLogger(AzureCopyFeature.class); private final AzureSession session; - private final OperationContext context; - private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - public AzureCopyFeature(final AzureSession session, final OperationContext context) { + public AzureCopyFeature(final AzureSession session) { this.session = session; - this.context = context; } @Override public Path copy(final Path source, final Path copy, final TransferStatus status, final ConnectionCallback callback, final StreamListener listener) throws BackgroundException { try { - final CloudBlob target = session.getClient().getContainerReference(containerService.getContainer(copy).getName()) - .getAppendBlobReference(containerService.getKey(copy)); - final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(source).getName()) - .getBlobReferenceFromServer(containerService.getKey(source)); - final BlobRequestOptions options = new BlobRequestOptions(); - options.setStoreBlobContentMD5(HostPreferencesFactory.get(session.getHost()).getBoolean("azure.upload.md5")); - final URI s = session.getHost().getCredentials().isTokenAuthentication() ? - URI.create(blob.getUri().toString() + session.getHost().getCredentials().getToken()) : blob.getUri(); - final String id = target.startCopy(s, - AccessCondition.generateEmptyCondition(), AccessCondition.generateEmptyCondition(), options, context); - log.debug("Started copy for {} with copy operation ID {}", copy, id); + final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(copy).getName()) + .getBlobClient(containerService.getKey(copy)); + final SyncPoller poller = client.beginCopy( + new BlobBeginCopyOptions(session.getClient().getBlobContainerClient(containerService.getContainer(source).getName()) + .getBlobClient(containerService.getKey(source)).getBlobUrl()).setPollInterval(Duration.ofSeconds(1))); + if(log.isDebugEnabled()) { + log.debug(String.format("Started copy for %s", copy)); + } + poller.waitForCompletion(); listener.sent(status.getLength()); - return copy; + return new Path(copy).withAttributes(PathAttributes.EMPTY); } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Cannot copy {0}", e, source); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } } @Override diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java index 4ef935d4b48..8774cbd6fa1 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureDeleteFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.DirectoryDelimiterPathContainerService; @@ -23,40 +20,30 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.transfer.TransferStatus; import org.apache.http.HttpStatus; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; -import com.microsoft.azure.storage.AccessCondition; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.DeleteSnapshotsOption; +import com.azure.core.exception.HttpResponseException; public class AzureDeleteFeature implements Delete { private final AzureSession session; - - private final OperationContext context; - private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - public AzureDeleteFeature(final AzureSession session, final OperationContext context) { + public AzureDeleteFeature(final AzureSession session) { this.session = session; - this.context = context; } @Override public void delete(final Map files, final PasswordCallback prompt, final Callback callback) throws BackgroundException { - final List containers = new ArrayList(); + final List containers = new ArrayList<>(); for(Path file : files.keySet()) { if(containerService.isContainer(file)) { containers.add(file); @@ -64,13 +51,11 @@ public void delete(final Map files, final PasswordCallback else { callback.delete(file); try { - final BlobRequestOptions options = new BlobRequestOptions(); - session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlockBlobReference(containerService.getKey(file)).delete( - DeleteSnapshotsOption.INCLUDE_SNAPSHOTS, AccessCondition.generateEmptyCondition(), options, context); + session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)).delete(); } - catch(StorageException e) { - switch(e.getHttpStatusCode()) { + catch(HttpResponseException e) { + switch(e.getResponse().getStatusCode()) { case HttpStatus.SC_NOT_FOUND: if(file.isPlaceholder()) { // Ignore failure with no placeholder object found @@ -79,24 +64,16 @@ public void delete(final Map files, final PasswordCallback } throw new AzureExceptionMappingService().map("Cannot delete {0}", e, file); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } } } for(Path file : containers) { callback.delete(file); try { - final BlobRequestOptions options = new BlobRequestOptions(); - session.getClient().getContainerReference(containerService.getContainer(file).getName()).delete( - AccessCondition.generateEmptyCondition(), options, context); + session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).delete(); } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Cannot delete {0}", e, file); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java index 56a3172a8e4..704cba53595 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureDirectoryFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.DirectoryDelimiterPathContainerService; @@ -23,8 +20,8 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; +import ch.cyberduck.core.exception.InteroperabilityException; import ch.cyberduck.core.exception.InvalidFilenameException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.transfer.TransferStatus; @@ -33,52 +30,43 @@ import org.apache.commons.lang3.RegExUtils; import org.apache.commons.lang3.StringUtils; -import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.EnumSet; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.azure.core.exception.HttpResponseException; public class AzureDirectoryFeature implements Directory { + private final AzureSession session; private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - private final AzureSession session; - private final OperationContext context; - private Write writer; - public AzureDirectoryFeature(final AzureSession session, final OperationContext context) { + public AzureDirectoryFeature(final AzureSession session) { this.session = session; - this.context = context; - this.writer = new AzureWriteFeature(session, context); + this.writer = new AzureWriteFeature(session); } @Override public Path mkdir(final Path folder, final TransferStatus status) throws BackgroundException { try { - final BlobRequestOptions options = new BlobRequestOptions(); if(containerService.isContainer(folder)) { // Container name must be lower case. - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(folder).getName()); - container.create(options, context); - return folder; + session.getClient().getBlobContainerClient(containerService.getContainer(folder).getName()).create(); + return new Path(folder.getParent(), folder.getName(), folder.getType()); } else { final EnumSet type = EnumSet.copyOf(folder.getType()); type.add(Path.Type.placeholder); - return new AzureTouchFeature(session, context).withWriter(writer).touch(folder.withType(type), + return new AzureTouchFeature(session).withWriter(writer).touch(folder.withType(type), status.setChecksum(writer.checksum(folder, status).compute(new NullInputStream(0L), status))); } } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); + catch(IllegalArgumentException e) { + throw new InteroperabilityException(); } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Cannot create folder {0}", e, folder); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java index 98cccb44c0d..b1b8d2997f8 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureExceptionMappingService.java @@ -1,61 +1,54 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.AbstractExceptionMappingService; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.LoginFailureException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.http.DefaultHttpResponseExceptionMappingService; import ch.cyberduck.core.ssl.SSLExceptionMappingService; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.apache.http.client.HttpResponseException; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import javax.net.ssl.SSLException; -import java.net.UnknownHostException; +import java.util.Map; -import com.microsoft.azure.storage.StorageException; +import com.azure.core.exception.HttpResponseException; -public class AzureExceptionMappingService extends AbstractExceptionMappingService { - private static final Logger log = LogManager.getLogger(AzureExceptionMappingService.class); +public class AzureExceptionMappingService extends AbstractExceptionMappingService { @Override - public BackgroundException map(final StorageException failure) { - log.warn("Map failure {}", failure.toString()); + public BackgroundException map(final HttpResponseException failure) { final StringBuilder buffer = new StringBuilder(); - this.append(buffer, failure.getMessage()); - if(ExceptionUtils.getRootCause(failure) instanceof UnknownHostException) { - return new NotfoundException(buffer.toString(), failure); + switch(failure.getResponse().getStatusCode()) { + case 403: + if(failure.getValue() instanceof Map) { + final Map messages = (Map) failure.getValue(); + if(messages.containsKey("Message")) { + this.append(buffer, messages.get("Message").toString()); + } + } + return new LoginFailureException(buffer.toString(), failure); } + this.append(buffer, failure.getMessage()); for(Throwable cause : ExceptionUtils.getThrowableList(failure)) { if(cause instanceof SSLException) { return new SSLExceptionMappingService().map(buffer.toString(), (SSLException) cause); } } - switch(failure.getHttpStatusCode()) { - case 403: - return new LoginFailureException(buffer.toString(), failure); - default: - return new DefaultHttpResponseExceptionMappingService().map(new HttpResponseException(failure.getHttpStatusCode(), buffer.toString())); - } + return new DefaultHttpResponseExceptionMappingService().map(failure, buffer, failure.getResponse().getStatusCode()); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java index 5bab17ff577..f7d3bf5aa26 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureFindFeature.java @@ -1,56 +1,42 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ -import ch.cyberduck.core.CancellingListProgressListener; import ch.cyberduck.core.DirectoryDelimiterPathContainerService; import ch.cyberduck.core.ListProgressListener; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.ListCanceledException; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Find; -import org.apache.http.HttpStatus; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.net.URISyntaxException; - -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.blob.CloudBlobContainer; - public class AzureFindFeature implements Find { private static final Logger log = LogManager.getLogger(AzureFindFeature.class); private final AzureSession session; - private final OperationContext context; - + private final AzureAttributesFinderFeature attributes; private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - public AzureFindFeature(final AzureSession session, final OperationContext context) { + public AzureFindFeature(final AzureSession session) { this.session = session; - this.context = context; + this.attributes = new AzureAttributesFinderFeature(session); } @Override @@ -59,46 +45,12 @@ public boolean find(Path file, final ListProgressListener listener) throws Backg return true; } try { - try { - if(containerService.isContainer(file)) { - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName()); - return container.exists(null, null, context); - } - if(file.isFile() || file.isPlaceholder()) { - try { - final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlobReferenceFromServer(containerService.getKey(file)); - return blob.exists(null, null, context); - } - catch(StorageException e) { - switch(e.getHttpStatusCode()) { - case HttpStatus.SC_NOT_FOUND: - if(file.isPlaceholder()) { - // Ignore failure and look for common prefix - break; - } - default: - throw e; - } - } - } - log.debug("Search for common prefix {}", file); - // Check for common prefix - try { - new AzureObjectListService(session, context).list(file, new CancellingListProgressListener()); - return true; - } - catch(ListCanceledException l) { - // Found common prefix - return true; - } - } - catch(StorageException e) { - throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file); - } - catch(URISyntaxException e) { - return false; + final boolean found; + if(containerService.isContainer(file)) { + return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).exists(); } + attributes.find(file, listener); + return true; } catch(NotfoundException e) { return false; diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java index ee61da4c84b..6b5f180eda8 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureListService.java @@ -1,7 +1,7 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2018 iterate GmbH. All rights reserved. + * Copyright (c) 2002-2024 iterate GmbH. All rights reserved. * https://cyberduck.io/ * * This program is free software; you can redistribute it and/or modify @@ -21,25 +21,21 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.BackgroundException; -import com.microsoft.azure.storage.OperationContext; - public class AzureListService implements ListService { private final AzureSession session; - private final OperationContext context; - public AzureListService(final AzureSession session, final OperationContext context) { + public AzureListService(final AzureSession session) { this.session = session; - this.context = context; } @Override public AttributedList list(final Path directory, final ListProgressListener listener) throws BackgroundException { if(directory.isRoot()) { - return new AzureContainerListService(session, context).list(directory, listener); + return new AzureContainerListService(session).list(directory, listener); } else { - return new AzureObjectListService(session, context).list(directory, listener); + return new AzureObjectListService(session).list(directory, listener); } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java index 0015e232427..41673226c3a 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureLoggingFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.Path; @@ -26,29 +23,25 @@ import java.util.Collections; import java.util.EnumSet; -import com.microsoft.azure.storage.LoggingOperations; -import com.microsoft.azure.storage.LoggingProperties; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.ServiceProperties; -import com.microsoft.azure.storage.StorageException; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.models.BlobAnalyticsLogging; +import com.azure.storage.blob.models.BlobRetentionPolicy; +import com.azure.storage.blob.models.BlobServiceProperties; public class AzureLoggingFeature implements Logging { private final AzureSession session; - private final OperationContext context; - - public AzureLoggingFeature(final AzureSession session, final OperationContext context) { + public AzureLoggingFeature(final AzureSession session) { this.session = session; - this.context = context; } @Override public LoggingConfiguration getConfiguration(final Path container) throws BackgroundException { try { - final ServiceProperties properties = session.getClient().downloadServiceProperties(null, context); + final BlobServiceProperties properties = session.getClient().getProperties(); final LoggingConfiguration configuration = new LoggingConfiguration( - !properties.getLogging().getLogOperationTypes().isEmpty(), + properties.getLogging().isRead() || properties.getLogging().isWrite() || properties.getLogging().isDelete(), "$logs" ); // When you have configured Storage Logging to log request data from your storage account, it saves the log data @@ -58,7 +51,7 @@ public LoggingConfiguration getConfiguration(final Path container) throws Backgr ); return configuration; } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Cannot read container configuration", e); } } @@ -66,19 +59,18 @@ public LoggingConfiguration getConfiguration(final Path container) throws Backgr @Override public void setConfiguration(final Path container, final LoggingConfiguration configuration) throws BackgroundException { try { - final ServiceProperties properties = session.getClient().downloadServiceProperties(null, context); - final LoggingProperties l = new LoggingProperties(); - if(configuration.isEnabled()) { - l.setLogOperationTypes(EnumSet.allOf(LoggingOperations.class)); - } - else { - l.setLogOperationTypes(EnumSet.noneOf(LoggingOperations.class)); - } - properties.setLogging(l); - session.getClient().uploadServiceProperties(properties, null, context); + final BlobServiceProperties properties = session.getClient().getProperties(); + properties.setLogging(new BlobAnalyticsLogging() + .setVersion("2.0") + .setRetentionPolicy(new BlobRetentionPolicy().setEnabled(false)) + .setDelete(configuration.isEnabled()) + .setRead(configuration.isEnabled()) + .setWrite(configuration.isEnabled()) + ); + session.getClient().setProperties(properties); } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Failure to write attributes of {0}", e, container); } } -} \ No newline at end of file +} diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java index 98869d7fd96..2aa863c5e28 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureMetadataFeature.java @@ -1,28 +1,24 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.DirectoryDelimiterPathContainerService; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Headers; import ch.cyberduck.core.preferences.HostPreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; @@ -30,30 +26,23 @@ import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpHeaders; -import java.net.URISyntaxException; import java.util.HashMap; import java.util.Map; -import com.microsoft.azure.storage.AccessCondition; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobProperties; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.blob.CloudBlobContainer; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.models.BlobHttpHeaders; +import com.azure.storage.blob.models.BlobProperties; public class AzureMetadataFeature implements Headers { private final AzureSession session; - private final OperationContext context; - private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - public AzureMetadataFeature(final AzureSession session, final OperationContext context) { + public AzureMetadataFeature(final AzureSession session) { this.session = session; - this.context = context; } @Override @@ -65,30 +54,23 @@ public Map getDefault() { public Map getMetadata(final Path file) throws BackgroundException { try { if(containerService.isContainer(file)) { - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName()); - container.downloadAttributes(); - return container.getMetadata(); + return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()).getProperties().getMetadata(); } else { - final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlobReferenceFromServer(containerService.getKey(file)); - // Populates the blob properties and metadata - blob.downloadAttributes(null, null, context); - final Map metadata = new HashMap(blob.getMetadata()); - final BlobProperties properties = blob.getProperties(); - if(StringUtils.isNotBlank(properties.getCacheControl())) { - metadata.put(HttpHeaders.CACHE_CONTROL, properties.getCacheControl()); - } + final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)); + final BlobProperties properties = client.getProperties(); + final Map metadata = properties.getMetadata(); if(StringUtils.isNotBlank(properties.getContentType())) { metadata.put(HttpHeaders.CONTENT_TYPE, properties.getContentType()); } + if(StringUtils.isNotBlank(properties.getCacheControl())) { + metadata.put(HttpHeaders.CACHE_CONTROL, properties.getCacheControl()); + } return metadata; } } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Failure to read attributes of {0}", e, file); } } @@ -96,42 +78,31 @@ public Map getMetadata(final Path file) throws BackgroundExcepti @Override public void setMetadata(final Path file, final TransferStatus status) throws BackgroundException { try { - final BlobRequestOptions options = new BlobRequestOptions(); if(containerService.isContainer(file)) { - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName()); - container.setMetadata(new HashMap<>(status.getMetadata())); - container.uploadMetadata(AccessCondition.generateEmptyCondition(), options, context); + session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .setMetadata(new HashMap<>(status.getMetadata())); } else { - final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlobReferenceFromServer(containerService.getKey(file)); - // Populates the blob properties and metadata - blob.downloadAttributes(); - // Replace metadata + final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)); final HashMap pruned = new HashMap<>(); for(Map.Entry m : status.getMetadata().entrySet()) { - final BlobProperties properties = blob.getProperties(); if(HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(m.getKey())) { // Update properties - properties.setCacheControl(m.getValue()); + client.setHttpHeaders(new BlobHttpHeaders().setCacheControl(m.getValue())); continue; } if(HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(m.getKey())) { // Update properties - properties.setContentType(m.getValue()); + client.setHttpHeaders(new BlobHttpHeaders().setContentType(m.getValue())); continue; } pruned.put(m.getKey(), m.getValue()); } - blob.setMetadata(pruned); - blob.uploadMetadata(AccessCondition.generateEmptyCondition(), options, context); - blob.uploadProperties(); + client.setMetadata(pruned); } } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Failure to write attributes of {0}", e, file); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java index 71307c045bb..9fd914af5d8 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureMoveFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.ConnectionCallback; @@ -29,16 +26,14 @@ import java.util.Collections; import java.util.Optional; -import com.microsoft.azure.storage.OperationContext; - public class AzureMoveFeature implements Move { private final AzureCopyFeature proxy; private final AzureDeleteFeature delete; - public AzureMoveFeature(final AzureSession session, final OperationContext context) { - this.proxy = new AzureCopyFeature(session, context); - this.delete = new AzureDeleteFeature(session, context); + public AzureMoveFeature(final AzureSession session) { + this.proxy = new AzureCopyFeature(session); + this.delete = new AzureDeleteFeature(session); } @Override diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java index 50a22480873..2fbf35819fc 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureObjectListService.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.AttributedList; @@ -26,53 +23,43 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.PathNormalizer; -import ch.cyberduck.core.SimplePathPredicate; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.exception.NotfoundException; -import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.preferences.HostPreferencesFactory; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.net.URISyntaxException; import java.util.EnumSet; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.ResultContinuation; -import com.microsoft.azure.storage.ResultSegment; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobListingDetails; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.blob.CloudBlobContainer; -import com.microsoft.azure.storage.blob.CloudBlobDirectory; -import com.microsoft.azure.storage.blob.ListBlobItem; +import com.azure.core.exception.HttpResponseException; +import com.azure.core.http.rest.PagedIterable; +import com.azure.core.http.rest.PagedResponse; +import com.azure.storage.blob.BlobContainerClient; +import com.azure.storage.blob.models.BlobItem; +import com.azure.storage.blob.models.BlobListDetails; +import com.azure.storage.blob.models.ListBlobsOptions; public class AzureObjectListService implements ListService { private static final Logger log = LogManager.getLogger(AzureObjectListService.class); private final AzureSession session; - private final OperationContext context; - private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); + private final AzureAttributesFinderFeature attributes; - public AzureObjectListService(final AzureSession session, final OperationContext context) { + public AzureObjectListService(final AzureSession session) { this.session = session; - this.context = context; + this.attributes = new AzureAttributesFinderFeature(session); } @Override public AttributedList list(final Path directory, final ListProgressListener listener) throws BackgroundException { try { - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(directory).getName()); + final BlobContainerClient containerClient = session.getClient().getBlobContainerClient(containerService.getContainer(directory).getName()); final AttributedList children = new AttributedList<>(); - ResultContinuation token = null; - ResultSegment result; + PagedIterable result; String prefix = StringUtils.EMPTY; if(!containerService.isContainer(directory)) { prefix = containerService.getKey(directory); @@ -81,47 +68,49 @@ public AttributedList list(final Path directory, final ListProgressListene } } boolean hasDirectoryPlaceholder = containerService.isContainer(directory); - do { - final BlobRequestOptions options = new BlobRequestOptions(); - result = container.listBlobsSegmented(prefix, false, EnumSet.noneOf(BlobListingDetails.class), - HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"), token, options, context); - for(ListBlobItem object : result.getResults()) { - if(new SimplePathPredicate(new Path(object.getUri().getPath(), EnumSet.of(Path.Type.directory))).test(directory)) { - log.debug("Skip placeholder key {}", object); + String continuationToken = null; + for(PagedResponse response : containerClient.listBlobsByHierarchy(String.valueOf(Path.DELIMITER), new ListBlobsOptions() + .setDetails(new BlobListDetails().setRetrieveMetadata(true)) + .setPrefix(prefix) + .setMaxResultsPerPage(HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize")), null).iterableByPage(continuationToken, + HostPreferencesFactory.get(session.getHost()).getInteger("azure.listing.chunksize"))) { + for(BlobItem item : response.getElements()) { + if(StringUtils.equals(prefix, item.getName())) { + if(log.isDebugEnabled()) { + log.debug(String.format("Skip placeholder key %s", item)); + } hasDirectoryPlaceholder = true; continue; } - final PathAttributes attributes = new PathAttributes(); - if(object instanceof CloudBlob) { - final CloudBlob blob = (CloudBlob) object; - attributes.setSize(blob.getProperties().getLength()); - attributes.setModificationDate(blob.getProperties().getLastModified().getTime()); - attributes.setETag(blob.getProperties().getEtag()); - if(StringUtils.isNotBlank(blob.getProperties().getContentMD5())) { - attributes.setChecksum(Checksum.parse(Hex.encodeHexString(Base64.decodeBase64(blob.getProperties().getContentMD5())))); - } + final PathAttributes attr; + if(item.isPrefix()) { + attr = PathAttributes.EMPTY; + } + else { + attr = attributes.toAttributes(item.getProperties()); } // A directory is designated by a delimiter character. - final EnumSet types = object instanceof CloudBlobDirectory + final EnumSet types = null != item.isPrefix() && item.isPrefix() ? EnumSet.of(Path.Type.directory, Path.Type.placeholder) : EnumSet.of(Path.Type.file); - final Path child = new Path(directory, PathNormalizer.name(object.getUri().getPath()), types, attributes); + final Path child = new Path(directory, PathNormalizer.name(item.getName()), types, attr); children.add(child); } listener.chunk(directory, children); - token = result.getContinuationToken(); + continuationToken = response.getContinuationToken(); + if(StringUtils.isBlank(continuationToken)) { + break; + } } - while(result.getHasMoreResults()); if(!hasDirectoryPlaceholder && children.isEmpty()) { - log.warn("No placeholder found for directory {}", directory); + if(log.isWarnEnabled()) { + log.warn(String.format("No placeholder found for directory %s", directory)); + } throw new NotfoundException(directory.getAbsolute()); } return children; } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Listing directory {0} failed", e, directory); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java index ad8e937637b..a9745bb23c7 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureProtocol.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.AbstractProtocol; @@ -24,16 +21,13 @@ import ch.cyberduck.core.LocaleFactory; import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.PathContainerService; -import ch.cyberduck.core.Protocol; import ch.cyberduck.core.Scheme; import ch.cyberduck.core.text.DefaultLexicographicOrderComparator; -import java.util.Comparator; +import org.apache.commons.codec.binary.Base64; -import com.microsoft.azure.storage.core.Base64; -import com.google.auto.service.AutoService; +import java.util.Comparator; -@AutoService(Protocol.class) public class AzureProtocol extends AbstractProtocol { @Override @@ -80,7 +74,10 @@ public Scheme getScheme() { public boolean validate(final Credentials credentials, final LoginOptions options) { if(super.validate(credentials, options)) { if(options.password) { - return Base64.validateIsBase64String(credentials.getPassword()); + if(credentials.getPassword().length() % 4 != 0) { + return false; + } + return Base64.isBase64(credentials.getPassword()); } return true; } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java index 8116bb80e8c..e0068ea0669 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureReadFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.ConnectionCallback; @@ -23,86 +20,48 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Read; -import ch.cyberduck.core.io.StreamCopier; import ch.cyberduck.core.transfer.TransferStatus; -import ch.cyberduck.core.worker.DefaultExceptionMappingService; -import org.apache.commons.io.input.NullInputStream; -import org.apache.commons.io.input.ProxyInputStream; -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.io.InputStream; -import java.net.URISyntaxException; -import com.microsoft.azure.storage.AccessCondition; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobInputStream; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.core.SR; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.models.BlobRange; +import com.azure.storage.blob.models.BlobRequestConditions; public class AzureReadFeature implements Read { private static final Logger log = LogManager.getLogger(AzureReadFeature.class); private final AzureSession session; - - private final OperationContext context; - private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); - public AzureReadFeature(final AzureSession session, final OperationContext context) { + public AzureReadFeature(final AzureSession session) { this.session = session; - this.context = context; + } + + @Override + public boolean offset(final Path file) { + return true; } @Override public InputStream read(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { - final CloudBlob blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlobReferenceFromServer(containerService.getKey(file)); - if(0L == blob.getProperties().getLength()) { - return new NullInputStream(0L); - } - final BlobRequestOptions options = new BlobRequestOptions(); - options.setConcurrentRequestCount(1); - final BlobInputStream in = blob.openInputStream(AccessCondition.generateEmptyCondition(), options, context); if(status.isAppend()) { - try { - return StreamCopier.skip(in, status.getOffset()); - } - catch(IndexOutOfBoundsException e) { - // If offset is invalid - throw new DefaultExceptionMappingService().map("Download {0} failed", e, file); - } + return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)).openInputStream(new BlobRange(status.getOffset()), new BlobRequestConditions()); + } + else { + return session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)).openInputStream(); } - return new ProxyInputStream(in) { - @Override - protected void handleIOException(final IOException e) throws IOException { - if(StringUtils.equals(SR.STREAM_CLOSED, e.getMessage())) { - log.warn("Ignore failure {}", e.getMessage()); - return; - } - final Throwable cause = ExceptionUtils.getRootCause(e); - if(cause instanceof StorageException) { - throw new IOException(e.getMessage(), new AzureExceptionMappingService().map((StorageException) cause)); - } - throw e; - } - }; } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Download {0} failed", e, file); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java index 6d2b7a80680..dde9e74192c 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java @@ -1,90 +1,82 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ -import ch.cyberduck.core.CancellingListProgressListener; +import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; import ch.cyberduck.core.HostKeyCallback; import ch.cyberduck.core.ListService; import ch.cyberduck.core.LoginCallback; -import ch.cyberduck.core.Path; import ch.cyberduck.core.PreferencesUseragentProvider; import ch.cyberduck.core.Scheme; -import ch.cyberduck.core.UrlProvider; +import ch.cyberduck.core.azure.apache.ApacheHttpClient; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.ListCanceledException; -import ch.cyberduck.core.exception.LoginFailureException; -import ch.cyberduck.core.exception.NotfoundException; -import ch.cyberduck.core.features.AclPermission; -import ch.cyberduck.core.features.AttributesFinder; -import ch.cyberduck.core.features.Copy; -import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.features.Directory; -import ch.cyberduck.core.features.Find; -import ch.cyberduck.core.features.Headers; -import ch.cyberduck.core.features.Logging; -import ch.cyberduck.core.features.Metadata; -import ch.cyberduck.core.features.Move; -import ch.cyberduck.core.features.Read; -import ch.cyberduck.core.features.Touch; -import ch.cyberduck.core.features.Upload; -import ch.cyberduck.core.features.Write; -import ch.cyberduck.core.http.DisabledX509HostnameVerifier; -import ch.cyberduck.core.proxy.Proxy; +import ch.cyberduck.core.features.*; +import ch.cyberduck.core.http.HttpSession; import ch.cyberduck.core.proxy.ProxyFinder; -import ch.cyberduck.core.proxy.ProxyHostUrlProvider; -import ch.cyberduck.core.shared.DefaultHomeFinderService; -import ch.cyberduck.core.ssl.CustomTrustSSLProtocolSocketFactory; import ch.cyberduck.core.ssl.DefaultX509KeyManager; import ch.cyberduck.core.ssl.DisabledX509TrustManager; -import ch.cyberduck.core.ssl.SSLSession; import ch.cyberduck.core.ssl.X509KeyManager; import ch.cyberduck.core.ssl.X509TrustManager; import ch.cyberduck.core.threading.CancelCallback; import org.apache.http.HttpHeaders; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.slf4j.LoggerFactory; -import javax.net.ssl.HttpsURLConnection; -import java.net.InetSocketAddress; -import java.net.URI; -import java.net.URISyntaxException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.List; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.SendingRequestEvent; -import com.microsoft.azure.storage.StorageCredentials; -import com.microsoft.azure.storage.StorageCredentialsAccountAndKey; -import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature; -import com.microsoft.azure.storage.StorageEvent; -import com.microsoft.azure.storage.blob.CloudBlobClient; +import com.azure.core.credential.AzureSasCredential; +import com.azure.core.exception.HttpResponseException; +import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpPipelineBuilder; +import com.azure.core.http.HttpPipelineCallContext; +import com.azure.core.http.HttpPipelineNextPolicy; +import com.azure.core.http.HttpResponse; +import com.azure.core.http.policy.AddDatePolicy; +import com.azure.core.http.policy.AddHeadersPolicy; +import com.azure.core.http.policy.AzureSasCredentialPolicy; +import com.azure.core.http.policy.HttpPipelinePolicy; +import com.azure.core.http.policy.RequestIdPolicy; +import com.azure.storage.blob.BlobServiceAsyncClient; +import com.azure.storage.blob.BlobServiceClient; +import com.azure.storage.blob.BlobServiceVersion; +import com.azure.storage.blob.implementation.models.EncryptionScope; +import com.azure.storage.blob.models.AccountKind; +import com.azure.storage.blob.models.BlobContainerEncryptionScope; +import com.azure.storage.blob.models.CpkInfo; +import com.azure.storage.common.StorageSharedKeyCredential; +import com.azure.storage.common.implementation.Constants; +import com.azure.storage.common.policy.MetadataValidationPolicy; +import com.azure.storage.common.policy.RequestRetryOptions; +import com.azure.storage.common.policy.RequestRetryPolicy; +import com.azure.storage.common.policy.ResponseValidationPolicyBuilder; +import com.azure.storage.common.policy.StorageSharedKeyCredentialPolicy; +import reactor.core.publisher.Mono; -public class AzureSession extends SSLSession { +public class AzureSession extends HttpSession { private static final Logger log = LogManager.getLogger(AzureSession.class); - private final OperationContext context - = new OperationContext(); - - private StorageEvent listener; + private final CredentialsHttpPipelinePolicy authenticator + = new CredentialsHttpPipelinePolicy(); public AzureSession(final Host h) { super(h, new DisabledX509TrustManager(), new DefaultX509KeyManager()); @@ -94,140 +86,128 @@ public AzureSession(final Host h, final X509TrustManager trust, final X509KeyMan super(h, trust, key); } - static { - HttpsURLConnection.setDefaultSSLSocketFactory(new CustomTrustSSLProtocolSocketFactory(new DisabledX509TrustManager(), new DefaultX509KeyManager())); - HttpsURLConnection.setDefaultHostnameVerifier(new DisabledX509HostnameVerifier()); - HttpsURLConnection.setFollowRedirects(true); - } - @Override - protected CloudBlobClient connect(final ProxyFinder proxyfinder, final HostKeyCallback callback, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + protected BlobServiceClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + final HttpClientBuilder pool = builder.build(proxy, this, prompt); + final List policies = new ArrayList<>(); + policies.add(new RequestIdPolicy()); + policies.add(new RequestRetryPolicy(new RequestRetryOptions())); + policies.add(new AddDatePolicy()); + policies.add(new AddHeadersPolicy(new com.azure.core.http.HttpHeaders( + Collections.singletonMap(HttpHeaders.USER_AGENT, new PreferencesUseragentProvider().get())) + )); + policies.add(new MetadataValidationPolicy()); + policies.add(authenticator); + policies.add(new ResponseValidationPolicyBuilder() + .addOptionalEcho(Constants.HeaderConstants.CLIENT_REQUEST_ID) + .addOptionalEcho(Constants.HeaderConstants.ENCRYPTION_KEY_SHA256) + .build()); + final HttpPipeline pipeline = new HttpPipelineBuilder() + .httpClient(new ApacheHttpClient(pool)) + .policies(policies.toArray(new HttpPipelinePolicy[0])) + .build(); try { - final StorageCredentials credentials; - if(host.getCredentials().isTokenAuthentication()) { - credentials = new StorageCredentialsSharedAccessSignature(host.getCredentials().getToken()); - } - else { - credentials = new StorageCredentialsAccountAndKey(host.getCredentials().getUsername(), "null"); - } - final URI uri = new URI(String.format("%s://%s", Scheme.https, host.getHostname())); - final CloudBlobClient client = new CloudBlobClient(uri, credentials); - client.setDirectoryDelimiter(String.valueOf(Path.DELIMITER)); - context.setLoggingEnabled(true); - context.setLogger(LoggerFactory.getLogger(log.getName())); - context.setUserHeaders(new HashMap<>(Collections.singletonMap( - HttpHeaders.USER_AGENT, new PreferencesUseragentProvider().get())) - ); - context.getSendingRequestEventHandler().addListener(listener = new StorageEvent() { - @Override - public void eventOccurred(final SendingRequestEvent event) { - if(event.getConnectionObject() instanceof HttpsURLConnection) { - final HttpsURLConnection connection = (HttpsURLConnection) event.getConnectionObject(); - connection.setSSLSocketFactory(new CustomTrustSSLProtocolSocketFactory(trust, key)); - connection.setHostnameVerifier(new DisabledX509HostnameVerifier()); - } - } - }); - final Proxy proxy = proxyfinder.find(new ProxyHostUrlProvider().get(host)); - switch(proxy.getType()) { - case SOCKS: { - log.info("Configured to use SOCKS proxy {}", proxyfinder); - final java.net.Proxy socksProxy = new java.net.Proxy( - java.net.Proxy.Type.SOCKS, new InetSocketAddress(proxy.getHostname(), proxy.getPort())); - context.setProxy(socksProxy); - break; - } - case HTTP: - case HTTPS: { - log.info("Configured to use HTTP proxy {}", proxyfinder); - final java.net.Proxy httpProxy = new java.net.Proxy( - java.net.Proxy.Type.HTTP, new InetSocketAddress(proxy.getHostname(), proxy.getPort())); - context.setProxy(httpProxy); - break; - } - } - return client; + final Constructor blobServiceAsyncClientConstructor = BlobServiceAsyncClient.class.getDeclaredConstructor(HttpPipeline.class, String.class, BlobServiceVersion.class, String.class, CpkInfo.class, EncryptionScope.class, BlobContainerEncryptionScope.class, boolean.class); + blobServiceAsyncClientConstructor.setAccessible(true); + final BlobServiceAsyncClient serviceAsyncClient = blobServiceAsyncClientConstructor.newInstance(pipeline, String.format("%s://%s", Scheme.https, host.getHostname()), + BlobServiceVersion.getLatest(), null, null, null, null, false); + final Constructor blobServiceClientConstructor = BlobServiceClient.class.getDeclaredConstructor(BlobServiceAsyncClient.class); + blobServiceClientConstructor.setAccessible(true); + return blobServiceClientConstructor.newInstance(serviceAsyncClient); } - catch(URISyntaxException e) { - throw new LoginFailureException(e.getMessage(), e); + catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + throw new BackgroundException(e); + } + } + + private static final class CredentialsHttpPipelinePolicy implements HttpPipelinePolicy { + private Credentials credentials = new Credentials(); + + public void setCredentials(final Credentials credentials) { + this.credentials = credentials; + } + + @Override + public Mono process(final HttpPipelineCallContext context, final HttpPipelineNextPolicy next) { + if(credentials.isTokenAuthentication()) { + return new AzureSasCredentialPolicy(new AzureSasCredential( + credentials.getToken())).process(context, next); + } + return new StorageSharedKeyCredentialPolicy(new StorageSharedKeyCredential( + credentials.getUsername(), credentials.getPassword())).process(context, next); } } @Override public void login(final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { - final StorageCredentials credentials = client.getCredentials(); - if(host.getCredentials().isPasswordAuthentication()) { - // Update credentials - final StorageCredentialsAccountAndKey method = (StorageCredentialsAccountAndKey) credentials; - method.updateKey(host.getCredentials().getPassword()); - } - // Fetch reference for directory to check login credentials + // Keep copy of credentials + authenticator.setCredentials(new Credentials(host.getCredentials())); try { - new AzureListService(this, context).list(new DefaultHomeFinderService(this).find(), new CancellingListProgressListener()); - } - catch(ListCanceledException e) { - // Success + final AccountKind kind = client.getAccountInfo().getAccountKind(); + if(log.isInfoEnabled()) { + log.info(String.format("Connected to account of kind %s", kind)); + } } - catch(NotfoundException e) { - log.warn("Ignore failure {}", e.getMessage()); + catch(HttpResponseException e) { + throw new AzureExceptionMappingService().map(e); } } @Override protected void logout() { - context.getSendingRequestEventHandler().removeListener(listener); + // } @Override @SuppressWarnings("unchecked") public T _getFeature(final Class type) { if(type == ListService.class) { - return (T) new AzureListService(this, context); + return (T) new AzureListService(this); } if(type == Read.class) { - return (T) new AzureReadFeature(this, context); + return (T) new AzureReadFeature(this); } if(type == Upload.class) { - return (T) new AzureUploadFeature(this, context); + return (T) new AzureUploadFeature(this); } if(type == Write.class) { - return (T) new AzureWriteFeature(this, context); + return (T) new AzureWriteFeature(this); } if(type == Directory.class) { - return (T) new AzureDirectoryFeature(this, context); + return (T) new AzureDirectoryFeature(this); } if(type == Delete.class) { - return (T) new AzureDeleteFeature(this, context); + return (T) new AzureDeleteFeature(this); } if(type == Headers.class) { - return (T) new AzureMetadataFeature(this, context); + return (T) new AzureMetadataFeature(this); } if(type == Metadata.class) { - return (T) new AzureMetadataFeature(this, context); + return (T) new AzureMetadataFeature(this); } if(type == Find.class) { - return (T) new AzureFindFeature(this, context); + return (T) new AzureFindFeature(this); } if(type == AttributesFinder.class) { - return (T) new AzureAttributesFinderFeature(this, context); + return (T) new AzureAttributesFinderFeature(this); } if(type == Logging.class) { - return (T) new AzureLoggingFeature(this, context); + return (T) new AzureLoggingFeature(this); } if(type == Move.class) { - return (T) new AzureMoveFeature(this, context); + return (T) new AzureMoveFeature(this); } if(type == Copy.class) { - return (T) new AzureCopyFeature(this, context); + return (T) new AzureCopyFeature(this); } if(type == Touch.class) { - return (T) new AzureTouchFeature(this, context); + return (T) new AzureTouchFeature(this); } - if(type == UrlProvider.class) { + if(type == Share.class) { return (T) new AzureUrlProvider(this); } if(type == AclPermission.class) { - return (T) new AzureAclPermissionFeature(this, context); + return (T) new AzureAclPermissionFeature(this); } return super._getFeature(type); } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java index da0eb43af0b..9b32f160833 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureTouchFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.LocaleFactory; @@ -29,12 +26,10 @@ import java.text.MessageFormat; -import com.microsoft.azure.storage.OperationContext; - public class AzureTouchFeature extends DefaultTouchFeature { - public AzureTouchFeature(final AzureSession session, final OperationContext context) { - super(new AzureWriteFeature(session, context)); + public AzureTouchFeature(final AzureSession session) { + super(new AzureWriteFeature(session)); } @Override @@ -46,7 +41,6 @@ public void preflight(final Path workdir, final String filename) throws Backgrou @Override public Path touch(final Path file, final TransferStatus status) throws BackgroundException { - status.setChecksum(write.checksum(file, status).compute(new NullInputStream(0L), status)); - return super.touch(file, status); + return super.touch(file, status.setChecksum(write.checksum(file, status).compute(new NullInputStream(0L), status))); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java index 39d8f6af62a..74e0a72f051 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureUploadFeature.java @@ -22,25 +22,22 @@ import ch.cyberduck.core.shared.DefaultUploadFeature; import ch.cyberduck.core.transfer.TransferStatus; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.blob.BlobType; +import com.azure.storage.blob.models.BlobType; public class AzureUploadFeature extends DefaultUploadFeature { private final AzureSession session; - private final OperationContext context; - public AzureUploadFeature(final AzureSession session, final OperationContext context) { - super(new AzureWriteFeature(session, context)); + public AzureUploadFeature(final AzureSession session) { + super(new AzureWriteFeature(session)); this.session = session; - this.context = context; } @Override public Write.Append append(final Path file, final TransferStatus status) throws BackgroundException { final Write.Append append = new Write.Append(status.isExists()).withStatus(status); if(append.append) { - final PathAttributes attr = new AzureAttributesFinderFeature(session, context).find(file); + final PathAttributes attr = new AzureAttributesFinderFeature(session).find(file); if(BlobType.APPEND_BLOB == BlobType.valueOf(attr.getCustom().get(AzureAttributesFinderFeature.KEY_BLOB_TYPE))) { return append; } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java index 7ba78b3152b..a24edb2e62c 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureUrlProvider.java @@ -1,27 +1,26 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.DescriptiveUrl; import ch.cyberduck.core.DescriptiveUrlBag; import ch.cyberduck.core.DirectoryDelimiterPathContainerService; +import ch.cyberduck.core.HostPasswordStore; import ch.cyberduck.core.LocaleFactory; +import ch.cyberduck.core.PasswordStoreFactory; import ch.cyberduck.core.Path; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.Scheme; @@ -30,46 +29,58 @@ import ch.cyberduck.core.UserDateFormatterFactory; import ch.cyberduck.core.preferences.HostPreferencesFactory; -import java.net.URISyntaxException; -import java.security.InvalidKeyException; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import java.text.MessageFormat; +import java.time.Duration; +import java.time.OffsetDateTime; import java.util.Calendar; import java.util.EnumSet; import java.util.Objects; import java.util.TimeZone; import java.util.concurrent.TimeUnit; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.blob.CloudBlobContainer; -import com.microsoft.azure.storage.blob.SharedAccessBlobPermissions; -import com.microsoft.azure.storage.blob.SharedAccessBlobPolicy; +import com.azure.storage.blob.implementation.util.BlobSasImplUtil; +import com.azure.storage.blob.sas.BlobSasPermission; +import com.azure.storage.blob.sas.BlobServiceSasSignatureValues; +import com.azure.storage.common.StorageSharedKeyCredential; public class AzureUrlProvider implements UrlProvider { + private static final Logger log = LogManager.getLogger(AzureUrlProvider.class); private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); private final AzureSession session; + private final HostPasswordStore store; public AzureUrlProvider(final AzureSession session) { + this(session, PasswordStoreFactory.get()); + } + + public AzureUrlProvider(final AzureSession session, final HostPasswordStore store) { this.session = session; + this.store = store; } @Override public DescriptiveUrlBag toUrl(final Path file, final EnumSet types) { final DescriptiveUrlBag list = new DescriptiveUrlBag(); - // In one hour - list.add(this.toSignedUrl(file, (int) TimeUnit.HOURS.toSeconds(1))); - // Default signed URL expiring in 24 hours. - list.add(this.toSignedUrl(file, (int) TimeUnit.SECONDS.toSeconds( - HostPreferencesFactory.get(session.getHost()).getInteger("s3.url.expire.seconds")))); - // 1 Week - list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(7))); - // 1 Month - list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(30))); - // 1 Year - list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(365))); + if(types.contains(DescriptiveUrl.Type.signed)) { + // In one hour + list.add(this.toSignedUrl(file, (int) TimeUnit.HOURS.toSeconds(1))); + // Default signed URL expiring in 24 hours. + list.add(this.toSignedUrl(file, (int) TimeUnit.SECONDS.toSeconds( + HostPreferencesFactory.get(session.getHost()).getInteger("s3.url.expire.seconds")))); + // 1 Week + list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(7))); + // 1 Month + list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(30))); + // 1 Year + list.add(this.toSignedUrl(file, (int) TimeUnit.DAYS.toSeconds(365))); + } return list; } @@ -77,17 +88,17 @@ private DescriptiveUrl toSignedUrl(final Path file, int seconds) { return new SharedAccessSignatureUrl(file, this.getExpiry(seconds)); } - protected Calendar getExpiry(final int seconds) { + protected Long getExpiry(final int seconds) { final Calendar expiry = Calendar.getInstance(TimeZone.getTimeZone("UTC")); expiry.add(Calendar.SECOND, seconds); - return expiry; + return expiry.getTimeInMillis(); } private final class SharedAccessSignatureUrl extends DescriptiveUrl { private final Path file; - private final Calendar expiry; + private final Long expiry; - public SharedAccessSignatureUrl(final Path file, final Calendar expiry) { + public SharedAccessSignatureUrl(final Path file, final Long expiry) { super(EMPTY); this.file = file; this.expiry = expiry; @@ -95,38 +106,25 @@ public SharedAccessSignatureUrl(final Path file, final Calendar expiry) { @Override public String getUrl() { - try { - if(!session.isConnected()) { - return DescriptiveUrl.EMPTY.getUrl(); + final String secret = store.findLoginPassword(session.getHost()); + if(StringUtils.isBlank(secret)) { + if(log.isWarnEnabled()) { + log.warn("No secret found in password store required to sign temporary URL"); } - final CloudBlobContainer container = session.getClient().getContainerReference(containerService.getContainer(file).getName()); - final String token; - if(containerService.isContainer(file)) { - token = container.generateSharedAccessSignature(this.getPolicy(expiry), null); - } - else { - final CloudBlob blob = container.getBlobReferenceFromServer(containerService.getKey(file)); - token = blob.generateSharedAccessSignature(this.getPolicy(expiry), null); - } - return String.format("%s://%s%s?%s", - Scheme.https.name(), session.getHost().getHostname(), URIEncoder.encode(file.getAbsolute()), token); - } - catch(InvalidKeyException | URISyntaxException | StorageException e) { return DescriptiveUrl.EMPTY.getUrl(); } - } - - private SharedAccessBlobPolicy getPolicy(final Calendar expiry) { - final SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy(); - policy.setSharedAccessExpiryTime(expiry.getTime()); - policy.setPermissions(EnumSet.of(SharedAccessBlobPermissions.READ)); - return policy; + final String token = new BlobSasImplUtil(new BlobServiceSasSignatureValues( + OffsetDateTime.now().plus(Duration.ofMillis(expiry)), new BlobSasPermission().setReadPermission(true)), containerService.getContainer(file).getName()) + .generateSas(new StorageSharedKeyCredential(session.getHost().getCredentials().getUsername(), + secret), null); + return String.format("%s://%s%s?%s", + Scheme.https.name(), session.getHost().getHostname(), URIEncoder.encode(file.getAbsolute()), token); } @Override public String getHelp() { return MessageFormat.format(LocaleFactory.localizedString("Expires {0}", "S3"), - UserDateFormatterFactory.get().getMediumFormat(expiry.getTimeInMillis())); + UserDateFormatterFactory.get().getMediumFormat(expiry)); } @Override diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java index 9a50ffbe555..163ca66ea87 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureWriteFeature.java @@ -1,21 +1,18 @@ package ch.cyberduck.core.azure; /* - * Copyright (c) 2002-2014 David Kocher. All rights reserved. - * http://cyberduck.io/ + * Copyright (c) 2002-2024 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 2 of the License, or + * 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. - * - * Bug fixes, suggestions and comments should be sent to: - * feedback@cyberduck.io */ import ch.cyberduck.core.ConnectionCallback; @@ -24,7 +21,8 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.PathContainerService; import ch.cyberduck.core.exception.BackgroundException; -import ch.cyberduck.core.exception.NotfoundException; +import ch.cyberduck.core.features.AttributesFinder; +import ch.cyberduck.core.features.Find; import ch.cyberduck.core.features.Write; import ch.cyberduck.core.io.Checksum; import ch.cyberduck.core.io.ChecksumCompute; @@ -33,47 +31,60 @@ import ch.cyberduck.core.io.StatusOutputStream; import ch.cyberduck.core.io.VoidStatusOutputStream; import ch.cyberduck.core.preferences.HostPreferencesFactory; +import ch.cyberduck.core.preferences.Preferences; +import ch.cyberduck.core.preferences.PreferencesFactory; import ch.cyberduck.core.transfer.TransferStatus; +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.http.HttpHeaders; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; -import java.net.URISyntaxException; -import java.util.EnumSet; import java.util.HashMap; +import java.util.Map; -import com.microsoft.azure.storage.AccessCondition; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.StorageException; -import com.microsoft.azure.storage.blob.BlobOutputStream; -import com.microsoft.azure.storage.blob.BlobRequestOptions; -import com.microsoft.azure.storage.blob.BlobType; -import com.microsoft.azure.storage.blob.CloudAppendBlob; -import com.microsoft.azure.storage.blob.CloudBlob; -import com.microsoft.azure.storage.blob.CloudBlockBlob; -import com.microsoft.azure.storage.core.SR; +import com.azure.core.exception.HttpResponseException; +import com.azure.storage.blob.BlobClient; +import com.azure.storage.blob.models.BlobHttpHeaders; +import com.azure.storage.blob.models.BlobType; +import com.azure.storage.blob.options.AppendBlobCreateOptions; +import com.azure.storage.blob.options.BlockBlobOutputStreamOptions; +import com.azure.storage.blob.specialized.AppendBlobClient; +import com.azure.storage.blob.specialized.BlobOutputStream; +import com.azure.storage.blob.specialized.BlockBlobClient; +import com.azure.storage.common.implementation.Constants; public class AzureWriteFeature implements Write { private static final Logger log = LogManager.getLogger(AzureWriteFeature.class); private final AzureSession session; - private final OperationContext context; + private final PathContainerService containerService = new DirectoryDelimiterPathContainerService(); + + private final Preferences preferences + = PreferencesFactory.get(); private final BlobType blobType; - public AzureWriteFeature(final AzureSession session, final OperationContext context) { - this(session, BlobType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("azure.upload.blobtype")), context); + public AzureWriteFeature(final AzureSession session) { + this(session, BlobType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("azure.upload.blobtype"))); + } + + public AzureWriteFeature(final AzureSession session, final BlobType blobType) { + this.session = session; + this.blobType = blobType; + } + + public AzureWriteFeature(final AzureSession session, final Find finder, final AttributesFinder attributes) { + this(session, finder, attributes, BlobType.valueOf(HostPreferencesFactory.get(session.getHost()).getProperty("azure.upload.blobtype"))); } - public AzureWriteFeature(final AzureSession session, final BlobType blobType, final OperationContext context) { + public AzureWriteFeature(final AzureSession session, final Find finder, final AttributesFinder attributes, final BlobType blobType) { this.session = session; this.blobType = blobType; - this.context = context; } @Override @@ -84,112 +95,110 @@ public ChecksumCompute checksum(final Path file, final TransferStatus status) { @Override public StatusOutputStream write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException { try { - final CloudBlob blob; + final BlobClient client = session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)); + final BlobHttpHeaders headers = new BlobHttpHeaders(); + if(StringUtils.isNotBlank(status.getMime())) { + headers.setContentType(status.getMime()); + } + final Map metadata = new HashMap<>(status.getMetadata()); + for(final Map.Entry m : metadata.entrySet()) { + if(HttpHeaders.CACHE_CONTROL.equalsIgnoreCase(m.getKey())) { + // Update properties + headers.setCacheControl(m.getValue()); + } + if(HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(m.getKey())) { + // Update properties + headers.setContentType(m.getValue()); + } + } + metadata.remove(HttpHeaders.CACHE_CONTROL); + metadata.remove(HttpHeaders.CONTENT_TYPE); + final Checksum checksum = status.getChecksum(); + if(Checksum.NONE != checksum) { + switch(checksum.algorithm) { + case md5: + try { + headers.setContentMd5(Hex.decodeHex(status.getChecksum().hash.toCharArray())); + } + catch(DecoderException e) { + // Ignore + } + break; + } + } + final BlobOutputStream out; if(status.isExists()) { - if(HostPreferencesFactory.get(session.getHost()).getBoolean("azure.upload.snapshot")) { - session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlobReferenceFromServer(containerService.getKey(file)).createSnapshot(); + if(preferences.getBoolean("azure.upload.snapshot")) { + session.getClient().getBlobContainerClient(containerService.getContainer(file).getName()) + .getBlobClient(containerService.getKey(file)).createSnapshot(); } if(status.isAppend()) { // Existing append blob type - blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getAppendBlobReference(containerService.getKey(file)); + out = client.getAppendBlobClient().getBlobOutputStream(); } else { // Existing block blob type - final PathAttributes attr = new AzureAttributesFinderFeature(session, context).find(file); + final PathAttributes attr = new AzureAttributesFinderFeature(session).find(file); if(BlobType.APPEND_BLOB == BlobType.valueOf(attr.getCustom().get(AzureAttributesFinderFeature.KEY_BLOB_TYPE))) { - blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getAppendBlobReference(containerService.getKey(file)); + out = client.getAppendBlobClient().getBlobOutputStream(true); } else { - blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlockBlobReference(containerService.getKey(file)); + final BlockBlobOutputStreamOptions options = new BlockBlobOutputStreamOptions() + .setMetadata(metadata) + .setHeaders(headers) + .setMetadata(metadata); + out = client.getBlockBlobClient().getBlobOutputStream(options); } } } else { // Create new blob with default type set in defaults switch(blobType) { - case APPEND_BLOB: - blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getAppendBlobReference(containerService.getKey(file)); + case APPEND_BLOB: { + final AppendBlobClient append = client.getAppendBlobClient(); + final AppendBlobCreateOptions options = new AppendBlobCreateOptions() + .setMetadata(metadata) + .setHeaders(headers) + .setMetadata(metadata); + append.createWithResponse(options, null, null); + out = append.getBlobOutputStream(); break; - default: - blob = session.getClient().getContainerReference(containerService.getContainer(file).getName()) - .getBlockBlobReference(containerService.getKey(file)); - } - } - if(StringUtils.isNotBlank(status.getMime())) { - blob.getProperties().setContentType(status.getMime()); - } - // Add previous metadata when overwriting file - final HashMap headers = new HashMap<>(status.getMetadata()); - blob.setMetadata(headers); - // Remove additional headers not allowed in metadata and move to properties - if(headers.containsKey(HttpHeaders.CACHE_CONTROL)) { - blob.getProperties().setCacheControl(headers.get(HttpHeaders.CACHE_CONTROL)); - headers.remove(HttpHeaders.CACHE_CONTROL); - } - if(headers.containsKey(HttpHeaders.CONTENT_TYPE)) { - blob.getProperties().setContentType(headers.get(HttpHeaders.CONTENT_TYPE)); - headers.remove(HttpHeaders.CONTENT_TYPE); - } - final Checksum checksum = status.getChecksum(); - if(Checksum.NONE != checksum) { - switch(checksum.algorithm) { - case md5: - headers.remove(HttpHeaders.CONTENT_MD5); - blob.getProperties().setContentMD5(status.getChecksum().base64); + } + default: { + final BlockBlobClient block = client.getBlockBlobClient(); + final BlockBlobOutputStreamOptions options = new BlockBlobOutputStreamOptions() + .setMetadata(metadata) + .setHeaders(headers) + .setMetadata(metadata); + out = block.getBlobOutputStream(options); break; - } - } - final BlobRequestOptions options = new BlobRequestOptions(); - options.setConcurrentRequestCount(1); - options.setStoreBlobContentMD5(HostPreferencesFactory.get(session.getHost()).getBoolean("azure.upload.md5")); - final BlobOutputStream out; - if(status.isAppend()) { - options.setStoreBlobContentMD5(false); - if(blob instanceof CloudAppendBlob) { - out = ((CloudAppendBlob) blob).openWriteExisting(AccessCondition.generateEmptyCondition(), options, context); - } - else { - throw new NotfoundException(String.format("Unexpected blob type for %s", blob.getName())); - } - } - else { - if(blob instanceof CloudAppendBlob) { - out = ((CloudAppendBlob) blob).openWriteNew(AccessCondition.generateEmptyCondition(), options, context); - } - else { - out = ((CloudBlockBlob) blob).openOutputStream(AccessCondition.generateEmptyCondition(), options, context); + } } } return new VoidStatusOutputStream(out) { + @Override + public void close() throws IOException { + try { + super.close(); + } + catch(RuntimeException e) { + this.handleIOException(new IOException(e.getMessage(), e)); + } + } + @Override protected void handleIOException(final IOException e) throws IOException { - if(StringUtils.equals(SR.STREAM_CLOSED, e.getMessage())) { - log.warn("Ignore failure {}", e.getMessage()); + if(StringUtils.equals(Constants.STREAM_CLOSED, e.getMessage())) { + log.warn(String.format("Ignore failure %s", e)); return; } - final Throwable cause = ExceptionUtils.getRootCause(e); - if(cause instanceof StorageException) { - throw new IOException(e.getMessage(), new AzureExceptionMappingService().map((StorageException) cause)); - } throw e; } }; } - catch(StorageException e) { + catch(HttpResponseException e) { throw new AzureExceptionMappingService().map("Upload {0} failed", e, file); } - catch(URISyntaxException e) { - throw new NotfoundException(e.getMessage(), e); - } - } - - @Override - public EnumSet features(final Path file) { - return EnumSet.of(Flags.checksum, Flags.mime); } } diff --git a/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java new file mode 100644 index 00000000000..9bdab1737dd --- /dev/null +++ b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpClient.java @@ -0,0 +1,80 @@ +package ch.cyberduck.core.azure.apache; + +/* + * Copyright (c) 2002-2024 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 com.azure.core.http.*; +import com.azure.core.util.FluxUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.protocol.HTTP; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; + +public class ApacheHttpClient implements HttpClient { + private final org.apache.http.client.HttpClient httpClient; + + public ApacheHttpClient(final HttpClientBuilder builder) { + this.httpClient = builder.build(); + } + + public Mono send(final HttpRequest azureRequest) { + try { + ApacheHttpRequest apacheRequest = new ApacheHttpRequest(azureRequest.getHttpMethod(), azureRequest.getUrl(), + azureRequest.getHeaders()); + + Mono bodyMono = (azureRequest.getBody() != null) + ? FluxUtil.collectBytesInByteBufferStream(azureRequest.getBody()) + : Mono.just(new byte[0]); + + return bodyMono.flatMap(bodyBytes -> { + apacheRequest.setEntity(new ByteArrayEntity(bodyBytes)); + try { + return Mono.just(new ApacheHttpResponse(azureRequest, httpClient.execute(apacheRequest))); + } catch (IOException ex) { + return Mono.error(ex); + } + }); + } catch (URISyntaxException e) { + return Mono.error(e); + } + } + + private static final class ApacheHttpRequest extends HttpEntityEnclosingRequestBase { + private final String method; + + private ApacheHttpRequest(HttpMethod method, URL url, HttpHeaders headers) throws URISyntaxException { + this.method = method.name(); + setURI(url.toURI()); + headers.stream().forEach(this::accept); + } + + @Override + public String getMethod() { + return method; + } + + private void accept(final HttpHeader header) { + if (!StringUtils.equalsIgnoreCase(header.getName(), HTTP.CONTENT_LEN)) { + this.addHeader(header.getName(), header.getValue()); + } + } + } +} diff --git a/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java new file mode 100644 index 00000000000..0ef4ffe3cf4 --- /dev/null +++ b/azure/src/main/java/ch/cyberduck/core/azure/apache/ApacheHttpResponse.java @@ -0,0 +1,79 @@ +package ch.cyberduck.core.azure.apache; + +/* + * Copyright (c) 2002-2024 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 com.azure.core.http.HttpHeaders; +import com.azure.core.http.HttpRequest; +import com.azure.core.http.HttpResponse; +import org.apache.http.HttpEntity; +import org.apache.http.util.EntityUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; + +final class ApacheHttpResponse extends HttpResponse { + private final int statusCode; + private final HttpHeaders headers; + private final HttpEntity entity; + + protected ApacheHttpResponse(HttpRequest request, org.apache.http.HttpResponse apacheResponse) { + super(request); + this.statusCode = apacheResponse.getStatusLine().getStatusCode(); + this.headers = new HttpHeaders(); + Arrays.stream(apacheResponse.getAllHeaders()) + .forEach(header -> headers.put(header.getName(), header.getValue())); + this.entity = apacheResponse.getEntity(); + } + + public int getStatusCode() { + return statusCode; + } + + public String getHeaderValue(String s) { + return headers.getValue(s); + } + + public HttpHeaders getHeaders() { + return headers; + } + + public Flux getBody() { + return getBodyAsByteArray().map(ByteBuffer::wrap).flux(); + } + + public Mono getBodyAsByteArray() { + if (null == entity) { + return Mono.empty(); + } + try { + return Mono.just(EntityUtils.toByteArray(entity)); + } catch (IOException e) { + return Mono.error(e); + } + } + + public Mono getBodyAsString() { + return getBodyAsByteArray().map(String::new); + } + + public Mono getBodyAsString(Charset charset) { + return getBodyAsByteArray().map(bytes -> new String(bytes, charset)); + } +} diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java index 9911084e379..cd7876e88ee 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AbstractAzureTest.java @@ -1,6 +1,4 @@ -package ch.cyberduck.core.azure; - -/* +package ch.cyberduck.core.azure;/* * Copyright (c) 2002-2021 iterate GmbH. All rights reserved. * https://cyberduck.io/ * @@ -23,18 +21,13 @@ import ch.cyberduck.core.DisabledProgressListener; import ch.cyberduck.core.Host; import ch.cyberduck.core.LoginConnectionService; -import ch.cyberduck.core.LoginOptions; import ch.cyberduck.core.cryptomator.CryptoVault; -import ch.cyberduck.core.ssl.DefaultX509KeyManager; -import ch.cyberduck.core.ssl.DisabledX509TrustManager; import ch.cyberduck.test.VaultTest; import org.junit.After; import org.junit.Before; import org.junit.runners.Parameterized; -import static org.junit.Assert.fail; - public class AbstractAzureTest extends VaultTest { protected AzureSession session; @@ -57,15 +50,8 @@ public void setup() throws Exception { final Host host = new Host(new AzureProtocol(), "kahy9boj3eib.blob.core.windows.net", new Credentials( PROPERTIES.get("azure.user"), PROPERTIES.get("azure.password") )); - session = new AzureSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); - final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() { - @Override - public Credentials prompt(final Host bookmark, final String username, final String title, final String reason, final LoginOptions options) { - fail(reason); - return null; - } - }, new DisabledHostKeyCallback(), - new DisabledPasswordStore(), new DisabledProgressListener()); - login.check(session, new DisabledCancelCallback()); + session = new AzureSession(host); + new LoginConnectionService(new DisabledLoginCallback(), new DisabledHostKeyCallback(), + new DisabledPasswordStore(), new DisabledProgressListener()).connect(session, new DisabledCancelCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java index cd37888d15b..a3090920c6a 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureAclPermissionFeatureTest.java @@ -1,5 +1,6 @@ package ch.cyberduck.core.azure; +import ch.cyberduck.core.Acl; import ch.cyberduck.core.AsciiRandomStringService; import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.NotfoundException; @@ -10,6 +11,8 @@ import java.util.EnumSet; +import static org.junit.Assert.assertEquals; + @Category(IntegrationTest.class) public class AzureAclPermissionFeatureTest extends AbstractAzureTest { @@ -17,7 +20,24 @@ public class AzureAclPermissionFeatureTest extends AbstractAzureTest { @Test(expected = NotfoundException.class) public void testReadNotFoundContainer() throws Exception { final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.volume, Path.Type.directory)); - final AzureAclPermissionFeature f = new AzureAclPermissionFeature(session, null); + final AzureAclPermissionFeature f = new AzureAclPermissionFeature(session); f.getPermission(container); } + + @Test + public void testWriteContainer() throws Exception { + final Path container = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); + final AzureAclPermissionFeature f = new AzureAclPermissionFeature(session); + { + final Acl acl = new Acl(); + acl.addAll(new Acl.GroupUser(Acl.GroupUser.EVERYONE), new Acl.Role(Acl.Role.READ)); + f.setPermission(container, acl); + assertEquals(acl, f.getPermission(container)); + } + { + final Acl acl = new Acl(); + f.setPermission(container, acl); + assertEquals(acl, f.getPermission(container)); + } + } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java index f862660d1c2..bd077cdca42 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureAttributesFinderFeatureTest.java @@ -27,14 +27,14 @@ public class AzureAttributesFinderFeatureTest extends AbstractAzureTest { @Test public void testFindRoot() throws Exception { - final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null); + final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session); assertEquals(PathAttributes.EMPTY, f.find(new Path("/", EnumSet.of(Path.Type.directory)))); } @Test(expected = NotfoundException.class) public void testNotFound() throws Exception { final Path container = new Path(StringUtils.lowerCase(new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.directory, Path.Type.volume)); - final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null); + final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session); f.find(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file))); } @@ -42,49 +42,49 @@ public void testNotFound() throws Exception { public void testFind() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null); + new AzureTouchFeature(session).touch(test, new TransferStatus()); + final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session); final PathAttributes attributes = f.find(test); assertEquals(0L, attributes.getSize()); assertNotNull(attributes.getETag()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testFindContainer() throws Exception { final Path container = new Path(new AlphanumericRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new AzureDirectoryFeature(session, null).mkdir(container, new TransferStatus()); - final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null); + new AzureDirectoryFeature(session).mkdir(container, new TransferStatus()); + final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session); final PathAttributes attributes = f.find(container); assertNotEquals(PathAttributes.EMPTY, attributes); assertNotNull(attributes.getETag()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testMissingPlaceholder() throws Exception { - final Path container = new AzureDirectoryFeature(session, null).mkdir( + final Path container = new AzureDirectoryFeature(session).mkdir( new Path(new AlphanumericRandomStringService().random().toLowerCase(Locale.ROOT), EnumSet.of(Path.Type.directory, Path.Type.volume)), new TransferStatus()); final String prefix = new AlphanumericRandomStringService().random(); final Path intermediate = new Path(container, prefix, EnumSet.of(Path.Type.directory)); - final Path directory = new AzureDirectoryFeature(session, null).mkdir(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(directory)); - final Path test = new AzureTouchFeature(session, null).touch( + final Path directory = new AzureDirectoryFeature(session).mkdir(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(directory)); + final Path test = new AzureTouchFeature(session).touch( new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session, null); + final AzureAttributesFinderFeature f = new AzureAttributesFinderFeature(session); final PathAttributes attributes = f.find(container); assertNotEquals(PathAttributes.EMPTY, attributes); assertNotNull(attributes.getETag()); - assertNotNull(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test))); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertNotNull(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test))); + assertNotNull(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test))); + new AzureDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertNotNull(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).find(new DefaultPathPredicate(test))); // Still found as prefix - assertNotNull(new AzureObjectListService(session, null).list(container, new DisabledListProgressListener()).find(new DefaultPathPredicate(intermediate))); - assertNotNull(new AzureObjectListService(session, null).list(intermediate, new DisabledListProgressListener()).find(new DefaultPathPredicate(directory))); + assertNotNull(new AzureObjectListService(session).list(container, new DisabledListProgressListener()).find(new DefaultPathPredicate(intermediate))); + assertNotNull(new AzureObjectListService(session).list(intermediate, new DisabledListProgressListener()).find(new DefaultPathPredicate(directory))); // Ignore 404 failures - assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session, null).find(directory)); - assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session, null).find(intermediate)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session).find(directory)); + assertSame(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session).find(intermediate)); + new AzureDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java index e45b49d5cde..476f9b76afc 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureCopyFeatureTest.java @@ -4,11 +4,14 @@ import ch.cyberduck.core.DisabledConnectionCallback; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.UnsupportedException; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.io.DisabledStreamListener; +import ch.cyberduck.core.io.StreamCancelation; import ch.cyberduck.core.io.StreamCopier; +import ch.cyberduck.core.io.StreamProgress; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -31,10 +34,9 @@ public class AzureCopyFeatureTest extends AbstractAzureTest { @Test public void testCopy() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new AzureTouchFeature(session, null).touch( + final Path test = new AzureTouchFeature(session).touch( new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - Thread.sleep(1000L); - final AzureCopyFeature feature = new AzureCopyFeature(session, null); + final AzureCopyFeature feature = new AzureCopyFeature(session); assertThrows(UnsupportedException.class, () -> feature.preflight(container, Optional.of(test))); try { feature.preflight(container, Optional.of(test)); @@ -45,30 +47,31 @@ public void testCopy() throws Exception { } final Path copy = feature.copy(test, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new DisabledConnectionCallback(), new DisabledStreamListener()); - assertEquals(test.attributes().getChecksum(), copy.attributes().getChecksum()); - assertNotEquals(new AzureAttributesFinderFeature(session, null).find(test).getModificationDate(), new AzureAttributesFinderFeature(session, null).find(copy).getModificationDate()); - assertTrue(new AzureFindFeature(session, null).find(test)); - assertTrue(new AzureFindFeature(session, null).find(copy)); - new AzureDeleteFeature(session, null).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertEquals(PathAttributes.EMPTY, copy.attributes()); + assertEquals(new AzureAttributesFinderFeature(session).find(test).getModificationDate(), + new AzureAttributesFinderFeature(session).find(copy).getModificationDate()); + assertTrue(new AzureFindFeature(session).find(test)); + assertTrue(new AzureFindFeature(session).find(copy)); + new AzureDeleteFeature(session).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testCopyToExistingFile() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new AzureDirectoryFeature(session, null).mkdir(folder, new TransferStatus()); - final Path test = new AzureTouchFeature(session, null).touch( + new AzureDirectoryFeature(session).mkdir(folder, new TransferStatus()); + final Path test = new AzureTouchFeature(session).touch( new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1023); - final OutputStream out = new AzureWriteFeature(session, null).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); - final Path copy = new AzureTouchFeature(session, null).touch( + final OutputStream out = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true).setLength(content.length), new DisabledConnectionCallback()); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out); + final Path copy = new AzureTouchFeature(session).touch( new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - new AzureCopyFeature(session, null).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); - assertEquals(1023L, new AzureAttributesFinderFeature(session, null).find(copy).getSize()); + new AzureCopyFeature(session).copy(test, copy, new TransferStatus().setExists(true), new DisabledConnectionCallback(), new DisabledStreamListener()); + assertEquals(1023L, new AzureAttributesFinderFeature(session).find(copy).getSize()); final Find find = new DefaultFindFeature(session); assertTrue(find.find(test)); assertTrue(find.find(copy)); - new AzureDeleteFeature(session, null).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Arrays.asList(test, copy), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java index ddaa7a9994b..347a96e2f0a 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureDeleteFeatureTest.java @@ -25,33 +25,33 @@ public class AzureDeleteFeatureTest extends AbstractAzureTest { @Test(expected = NotfoundException.class) public void testDeleteNotFoundBucket() throws Exception { final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test(expected = NotfoundException.class) public void testDeleteNotFoundKey() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testDeletePlaceholder() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(test)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new AzureFindFeature(session, null).find(test)); + final Path test = new AzureDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(test)); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertFalse(new AzureFindFeature(session).find(test)); } @Test public void testDeleteKey() throws Exception { final Path container = new Path(new AsciiRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)); - new AzureDirectoryFeature(session, null).mkdir(container, new TransferStatus()); + new AzureDirectoryFeature(session).mkdir(container, new TransferStatus()); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(test)); - new AzureDeleteFeature(session, null).delete(Arrays.asList(container, test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new AzureFindFeature(session, null).find(test)); + new AzureTouchFeature(session).touch(test, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(test)); + new AzureDeleteFeature(session).delete(Arrays.asList(container, test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertFalse(new AzureFindFeature(session).find(test)); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java index d10a7ed4fed..d30a2ac4506 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureDirectoryFeatureTest.java @@ -24,40 +24,42 @@ public class AzureDirectoryFeatureTest extends AbstractAzureTest { @Test public void testCreateContainer() throws Exception { - final AzureDirectoryFeature feature = new AzureDirectoryFeature(session, null); + final AzureDirectoryFeature feature = new AzureDirectoryFeature(session); final Path container = feature.mkdir(new Path(new AlphanumericRandomStringService().random().toLowerCase(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(container)); + assertTrue(new AzureFindFeature(session).find(container)); + assertEquals(PathAttributes.EMPTY, container.attributes()); assertThrows(ConflictException.class, () -> feature.mkdir(container, new TransferStatus())); - new AzureTouchFeature(session, null).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new AzureFindFeature(session, null).find(container)); + new AzureTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertFalse(new AzureFindFeature(session).find(container)); } @Test(expected = InteroperabilityException.class) public void testCreateContainerInvalidName() throws Exception { final Path container = new Path("untitled folder", EnumSet.of(Path.Type.directory)); - final AzureDirectoryFeature feature = new AzureDirectoryFeature(session, null); + final AzureDirectoryFeature feature = new AzureDirectoryFeature(session); assertFalse(feature.isSupported(container.getParent(), container.getName())); assertThrows(InvalidFilenameException.class, () -> feature.preflight(container.getParent(), container.getName())); feature.mkdir(container, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(container)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new AzureFindFeature(session, null).find(container)); + assertTrue(new AzureFindFeature(session).find(container)); + new AzureDeleteFeature(session).delete(Collections.singletonList(container), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertFalse(new AzureFindFeature(session).find(container)); } @Test public void testCreatePlaceholder() throws Exception { final Path container = new Path("/cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); - final Path placeholder = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), + final Path placeholder = new AzureDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(placeholder.getType().contains(Path.Type.placeholder)); - assertTrue(new AzureFindFeature(session, null).find(placeholder)); + assertTrue(new AzureFindFeature(session).find(placeholder)); + assertEquals(PathAttributes.EMPTY, placeholder.attributes()); final Path file = new Path(placeholder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(file, new TransferStatus()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureTouchFeature(session).touch(file, new TransferStatus()); + new AzureDeleteFeature(session).delete(Collections.singletonList(placeholder), new DisabledLoginCallback(), new Delete.DisabledCallback()); // Still find common prefix - assertTrue(new AzureFindFeature(session, null).find(placeholder)); - assertEquals(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session, null).find(placeholder)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertTrue(new AzureFindFeature(session).find(placeholder)); + assertEquals(PathAttributes.EMPTY, new AzureAttributesFinderFeature(session).find(placeholder)); + new AzureDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java index e253ecf22df..8af92f2af0f 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureFindFeatureTest.java @@ -41,55 +41,56 @@ public class AzureFindFeatureTest extends AbstractAzureTest { @Test public void testFindNotFound() throws Exception { - assertFalse(new AzureFindFeature(session, null).find(new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)))); + final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); + assertFalse(new AzureFindFeature(session).find(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)))); } @Test public void testFindHome() throws Exception { - assertTrue(new AzureFindFeature(session, null).find(new DefaultHomeFinderService(session).find())); + assertTrue(new AzureFindFeature(session).find(new DefaultHomeFinderService(session).find())); } @Test public void testFindDirectory() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path folder = new AzureDirectoryFeature(session, null).mkdir( + final Path folder = new AzureDirectoryFeature(session).mkdir( new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(folder)); - assertFalse(new AzureFindFeature(session, null).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertTrue(new AzureFindFeature(session).find(folder)); + assertFalse(new AzureFindFeature(session).find(new Path(folder.getAbsolute(), EnumSet.of(Path.Type.file)))); + new AzureDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testFindFile() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(file, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(file)); - assertFalse(new AzureFindFeature(session, null).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureTouchFeature(session).touch(file, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(file)); + assertFalse(new AzureFindFeature(session).find(new Path(file.getAbsolute(), EnumSet.of(Path.Type.directory)))); + new AzureDeleteFeature(session).delete(Collections.singletonList(file), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testFindCommonPrefix() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - assertTrue(new AzureFindFeature(session, null).find(container)); + assertTrue(new AzureFindFeature(session).find(container)); final String prefix = new AlphanumericRandomStringService().random(); final Path intermediate = new Path(container, prefix, EnumSet.of(Path.Type.directory)); - final Path test = new AzureTouchFeature(session, null).touch(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(test)); - assertFalse(new AzureFindFeature(session, null).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory)))); - assertTrue(new AzureFindFeature(session, null).find(intermediate)); + final Path test = new AzureTouchFeature(session).touch(new Path(intermediate, new AsciiRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(test)); + assertFalse(new AzureFindFeature(session).find(new Path(test.getAbsolute(), EnumSet.of(Path.Type.directory)))); + assertTrue(new AzureFindFeature(session).find(intermediate)); // Ignore 404 for placeholder and search for common prefix - assertTrue(new AzureFindFeature(session, null).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder)))); - assertTrue(new AzureObjectListService(session, null).list(intermediate, + assertTrue(new AzureFindFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder)))); + assertTrue(new AzureObjectListService(session).list(intermediate, new DisabledListProgressListener()).contains(test)); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - assertFalse(new AzureFindFeature(session, null).find(test)); - assertFalse(new AzureFindFeature(session, null).find(intermediate)); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertFalse(new AzureFindFeature(session).find(test)); + assertFalse(new AzureFindFeature(session).find(intermediate)); final PathCache cache = new PathCache(1); final Path directory = new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder)); - assertFalse(new CachingFindFeature(session, cache).find(directory)); - assertTrue(cache.isCached(directory.getParent())); - assertFalse(new AzureFindFeature(session, null).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder)))); + assertFalse(new CachingFindFeature(session, cache, new AzureFindFeature(session)).find(directory)); + assertFalse(cache.isCached(directory)); + assertFalse(new AzureFindFeature(session).find(new Path(container, prefix, EnumSet.of(Path.Type.directory, Path.Type.placeholder)))); } } \ No newline at end of file diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java index 1dca5e2df54..39abb86cf5d 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureLoggingFeatureTest.java @@ -18,7 +18,7 @@ public class AzureLoggingFeatureTest extends AbstractAzureTest { @Test public void testSetConfiguration() throws Exception { final Path container = new Path("/cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); - final AzureLoggingFeature feature = new AzureLoggingFeature(session, null); + final AzureLoggingFeature feature = new AzureLoggingFeature(session); feature.setConfiguration(container, new LoggingConfiguration(false)); assertFalse(feature.getConfiguration(container).isEnabled()); feature.setConfiguration(container, new LoggingConfiguration(true)); diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java index 726b8ad40aa..488da9a38d2 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureMetadataFeatureTest.java @@ -23,31 +23,27 @@ public class AzureMetadataFeatureTest extends AbstractAzureTest { public void testSetMetadata() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - final TransferStatus status = new TransferStatus(); + new AzureTouchFeature(session).touch(test, new TransferStatus()); final String v = new AlphanumericRandomStringService().random(); - final AzureMetadataFeature feature = new AzureMetadataFeature(session, null); - feature.setMetadata(test, status.setMetadata(Collections.singletonMap("Test", v))); - final Map metadata = feature.getMetadata(test); + new AzureMetadataFeature(session).setMetadata(test, Collections.singletonMap("Test", v)); + final Map metadata = new AzureMetadataFeature(session).getMetadata(test); assertFalse(metadata.isEmpty()); assertTrue(metadata.containsKey("Test")); assertEquals(v, metadata.get("Test")); - feature.setMetadata(test, status.setMetadata(Collections.emptyMap())); - assertFalse(feature.getMetadata(test).containsKey("Test")); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testSetCacheControl() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - final AzureMetadataFeature service = new AzureMetadataFeature(session, null); + new AzureTouchFeature(session).touch(test, new TransferStatus()); + final AzureMetadataFeature service = new AzureMetadataFeature(session); service.setMetadata(test, Collections.singletonMap("Cache-Control", "public, max-age=0")); final Map metadata = service.getMetadata(test); assertFalse(metadata.isEmpty()); assertTrue(metadata.containsKey("Cache-Control")); assertEquals("public, max-age=0", metadata.get("Cache-Control")); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java index 16f2b54cbb3..6f1ab998934 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureMoveFeatureTest.java @@ -6,6 +6,8 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.synchronization.Comparison; +import ch.cyberduck.core.synchronization.ComparisonService; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -16,8 +18,7 @@ import java.util.EnumSet; import java.util.Optional; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; @Category(IntegrationTest.class) public class AzureMoveFeatureTest extends AbstractAzureTest { @@ -25,20 +26,22 @@ public class AzureMoveFeatureTest extends AbstractAzureTest { @Test public void testMove() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new AzureTouchFeature(session, null).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(test)); - final Path target = new AzureMoveFeature(session, null).move(test, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); - assertFalse(new AzureFindFeature(session, null).find(test)); - assertTrue(new AzureFindFeature(session, null).find(target)); - final PathAttributes targetAttr = new AzureAttributesFinderFeature(session, null).find(target); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); + final Path test = new AzureTouchFeature(session).touch(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); + final PathAttributes sourceAttr = new AzureAttributesFinderFeature(session).find(test); + assertTrue(new AzureFindFeature(session).find(test)); + final Path target = new AzureMoveFeature(session).move(test, new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); + assertFalse(new AzureFindFeature(session).find(test)); + assertTrue(new AzureFindFeature(session).find(target)); + final PathAttributes targetAttr = new AzureAttributesFinderFeature(session).find(target); + assertEquals(Comparison.equal, session.getHost().getProtocol().getFeature(ComparisonService.class).compare(Path.Type.file, sourceAttr, targetAttr)); + new AzureDeleteFeature(session).delete(Collections.singletonList(target), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testSupport() { final Path c = new Path("/c", EnumSet.of(Path.Type.directory)); - assertFalse(new AzureMoveFeature(session, null).isSupported(c, Optional.of(new Path("/d", EnumSet.of(Path.Type.directory))))); + assertFalse(new AzureMoveFeature(session).isSupported(c, Optional.of(new Path("/d", EnumSet.of(Path.Type.directory))))); final Path cf = new Path("/c/f", EnumSet.of(Path.Type.directory)); - assertTrue(new AzureMoveFeature(session, null).isSupported(cf, Optional.of(new Path("/c/f2", EnumSet.of(Path.Type.directory))))); + assertTrue(new AzureMoveFeature(session).isSupported(cf, Optional.of(new Path("/c/f2", EnumSet.of(Path.Type.directory))))); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java index da56dc923bb..523c018922e 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureObjectListServiceTest.java @@ -6,9 +6,9 @@ import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.IndexedListProgressListener; import ch.cyberduck.core.Path; +import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.io.HashAlgorithm; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -30,15 +30,15 @@ public class AzureObjectListServiceTest extends AbstractAzureTest { @Test(expected = NotfoundException.class) public void testListNotFoundFolder() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - new AzureObjectListService(session, null).list(new Path(container, "notfound", EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); + new AzureObjectListService(session).list(new Path(container, "notfound", EnumSet.of(Path.Type.directory)), new DisabledListProgressListener()); } @Test public void testListEmptyFolder() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path folder = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path folder = new AzureDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final AtomicBoolean callback = new AtomicBoolean(); - assertTrue(new AzureObjectListService(session, null).list(folder, new DisabledListProgressListener() { + assertTrue(new AzureObjectListService(session).list(folder, new DisabledListProgressListener() { @Override public void chunk(final Path parent, final AttributedList list) { assertNotSame(AttributedList.EMPTY, list); @@ -46,45 +46,46 @@ public void chunk(final Path parent, final AttributedList list) { } }).isEmpty()); assertTrue(callback.get()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(folder), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test(expected = NotfoundException.class) public void testListNotfoundContainer() throws Exception { final Path container = new Path("notfound-cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - new AzureObjectListService(session, null).list(container, new DisabledListProgressListener()); + new AzureObjectListService(session).list(container, new DisabledListProgressListener()); } @Test public void testList() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path directory = new AzureDirectoryFeature(session, null).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path directory = new AzureDirectoryFeature(session).mkdir(new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); final Path file = new Path(directory, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - assertTrue(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).isEmpty()); - new AzureTouchFeature(session, null).touch(file, new TransferStatus()); - final AttributedList list = new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()); + assertTrue(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).isEmpty()); + new AzureTouchFeature(session).touch(file, new TransferStatus()); + final AttributedList list = new AzureObjectListService(session).list(directory, new DisabledListProgressListener()); assertFalse(list.isEmpty()); assertEquals(1, list.size()); assertTrue(list.contains(file)); - assertEquals(HashAlgorithm.md5, list.get(0).attributes().getChecksum().algorithm); + final PathAttributes attributes = list.get(0).attributes(); + assertEquals(attributes, new AzureAttributesFinderFeature(session).find(file)); assertSame(directory, list.get(0).getParent()); - new AzureDeleteFeature(session, null).delete(Arrays.asList(file, directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Arrays.asList(file, directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testListLexicographicSortOrderAssumption() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.volume, Path.Type.directory)); - final Path directory = new AzureDirectoryFeature(session, null).mkdir( + final Path directory = new AzureDirectoryFeature(session).mkdir( new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - assertTrue(new AzureObjectListService(session, null).list(directory, new DisabledListProgressListener()).isEmpty()); + assertTrue(new AzureObjectListService(session).list(directory, new DisabledListProgressListener()).isEmpty()); final List files = Arrays.asList( "Z", "aa", "0a", "a", "AAA", "B", "~$a", ".c" ); for(String f : files) { - new AzureTouchFeature(session, null).touch(new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus()); + new AzureTouchFeature(session).touch(new Path(directory, f, EnumSet.of(Path.Type.file)), new TransferStatus()); } files.sort(session.getHost().getProtocol().getListComparator()); - final AttributedList list = new AzureObjectListService(session, null).list(directory, new IndexedListProgressListener() { + final AttributedList list = new AzureObjectListService(session).list(directory, new IndexedListProgressListener() { @Override public void message(final String message) { // @@ -97,8 +98,8 @@ public void visit(final AttributedList list, final int index, final Path f }); for(int i = 0; i < list.size(); i++) { assertEquals(files.get(i), list.get(i).getName()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(list.get(i)), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(list.get(i)), new DisabledLoginCallback(), new Delete.DisabledCallback()); } - new AzureDeleteFeature(session, null).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(directory), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java index aa8184ff6d6..3411acbafbf 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureReadFeatureTest.java @@ -6,7 +6,9 @@ import ch.cyberduck.core.Path; import ch.cyberduck.core.exception.NotfoundException; import ch.cyberduck.core.features.Delete; +import ch.cyberduck.core.io.StreamCancelation; import ch.cyberduck.core.io.StreamCopier; +import ch.cyberduck.core.io.StreamProgress; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -31,34 +33,34 @@ public class AzureReadFeatureTest extends AbstractAzureTest { public void testReadNotFound() throws Exception { final TransferStatus status = new TransferStatus(); final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - new AzureReadFeature(session, null).read(new Path(container, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); + new AzureReadFeature(session).read(new Path(container, "nosuchname", EnumSet.of(Path.Type.file)), status, new DisabledConnectionCallback()); } @Test public void testReadZeroLength() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - final InputStream in = new AzureReadFeature(session, null).read(test, new TransferStatus().setLength(0L), new DisabledConnectionCallback()); + new AzureTouchFeature(session).touch(test, new TransferStatus()); + final InputStream in = new AzureReadFeature(session).read(test, new TransferStatus().setLength(0L), new DisabledConnectionCallback()); assertNotNull(in); in.close(); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testReadRange() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); + new AzureTouchFeature(session).touch(test, new TransferStatus()); final byte[] content = RandomUtils.nextBytes(1023); - final OutputStream out = new AzureWriteFeature(session, null).write(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + final OutputStream out = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true).setLength(content.length), new DisabledConnectionCallback()); assertNotNull(out); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out); final TransferStatus status = new TransferStatus(); status.setLength(content.length); status.setAppend(true); status.setOffset(100L); - final InputStream in = new AzureReadFeature(session, null).read(test, status, new DisabledConnectionCallback()); + final InputStream in = new AzureReadFeature(session).read(test, status, new DisabledConnectionCallback()); assertNotNull(in); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length - 100); new StreamCopier(status, status).transfer(in, buffer); @@ -68,6 +70,6 @@ public void testReadRange() throws Exception { in.close(); // Test double close in.close(); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java index 384b28259ee..a7a27293ab0 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureSessionTest.java @@ -9,16 +9,26 @@ import ch.cyberduck.core.Host; import ch.cyberduck.core.LoginConnectionService; import ch.cyberduck.core.LoginOptions; +import ch.cyberduck.core.Profile; +import ch.cyberduck.core.ProtocolFactory; import ch.cyberduck.core.exception.LoginCanceledException; import ch.cyberduck.core.features.AclPermission; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Touch; +import ch.cyberduck.core.serializer.impl.dd.ProfilePlistReader; +import ch.cyberduck.core.ssl.DefaultX509KeyManager; +import ch.cyberduck.core.ssl.DisabledX509TrustManager; import ch.cyberduck.test.IntegrationTest; +import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.experimental.categories.Category; +import java.util.Collections; +import java.util.HashSet; +import java.util.concurrent.atomic.AtomicBoolean; + import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -33,8 +43,59 @@ public void testFeatures() { } @Test - public void testConnect() throws Exception { + public void testConnect() { + assertTrue(session.isConnected()); + } + + @Test + public void testConnectSharedAccessSignature() throws Exception { + final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new AzureProtocol()))); + final Profile profile = new ProfilePlistReader(factory).read( + this.getClass().getResourceAsStream("/Azure (Shared Access Signature Token).cyberduckprofile")); + final Host host = new Host(profile, "kahy9boj3eib.blob.core.windows.net", new Credentials( + PROPERTIES.get("azure.user"), null, PROPERTIES.get("azure.token") + )); + AzureSession session = new AzureSession(host, new DisabledX509TrustManager(), new DefaultX509KeyManager()); + final LoginConnectionService login = new LoginConnectionService(new DisabledLoginCallback() { + @Override + public Credentials prompt(final Host bookmark, final String username, final String title, final String reason, final LoginOptions options) { + fail(reason); + return null; + } + }, new DisabledHostKeyCallback(), + new DisabledPasswordStore(), new DisabledProgressListener()); + login.connect(session, new DisabledCancelCallback()); + session.close(); + } + + @Test + public void testConnectSharedAccessSignaturePrompt() throws Exception { + final ProtocolFactory factory = new ProtocolFactory(new HashSet<>(Collections.singleton(new AzureProtocol()))); + final Profile profile = new ProfilePlistReader(factory).read( + this.getClass().getResourceAsStream("/Azure (Shared Access Signature Token).cyberduckprofile")); + final Host host = new Host(profile, "kahy9boj3eib.blob.core.windows.net", new Credentials( + null, null, "?sv=2017-07-29&ss=bfqt&srt=sco&sp=rwdlacup&se=2030-05-20T04:29:30Z&st=2018-05-09T20:29:30Z&spr=https&sig=invalidbMKAZ3tXmX%2B56%2Bb5JhHAeWnMOpMp%2BoYlHDIAZVAjHzE%3D")); + final AzureSession session = new AzureSession(host); + final AtomicBoolean prompt = new AtomicBoolean(); + final LoginConnectionService connect = new LoginConnectionService(new DisabledLoginCallback() { + @Override + public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) throws LoginCanceledException { + if(prompt.get()) { + throw new LoginCanceledException(); + } + try { + return new Credentials(StringUtils.EMPTY, "?sv=2017-07-29&ss=bfqt&srt=sco&sp=rwdlacup&se=2030-05-20T04:29:30Z&st=2018-05-09T20:29:30Z&spr=https&sig=bMKAZ3tXmX%2B56%2Bb5JhHAeWnMOpMp%2BoYlHDIAZVAjHzE%3D"); + } + finally { + prompt.set(true); + } + } + }, new DisabledHostKeyCallback(), + new DisabledPasswordStore(), new DisabledProgressListener()); + connect.connect(session, new DisabledCancelCallback()); assertTrue(session.isConnected()); + connect.close(session); + assertFalse(session.isConnected()); } @Test(expected = LoginCanceledException.class) @@ -47,7 +108,6 @@ public void testConnectInvalidKey() throws Exception { @Override public Credentials prompt(final Host bookmark, String username, String title, String reason, LoginOptions options) throws LoginCanceledException { assertEquals("Login kahy9boj3eib.blob.core.windows.net", title); - assertEquals("Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. Please contact your web hosting service provider for assistance.", reason); return super.prompt(bookmark, username, title, reason, options); } }, new DisabledHostKeyCallback(), @@ -64,7 +124,6 @@ public void testConnectKeyNotBase64() throws Exception { @Override public Credentials prompt(final Host bookmark, String username, String title, String reason, LoginOptions options) throws LoginCanceledException { assertEquals("Login kahy9boj3eib.blob.core.windows.net", title); - assertEquals("Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. Please contact your web hosting service provider for assistance.", reason); return super.prompt(bookmark, username, title, reason, options); } }, new DisabledHostKeyCallback(), diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java index d337a3b0f56..5e9d49a8be8 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureTouchFeatureTest.java @@ -36,14 +36,14 @@ public class AzureTouchFeatureTest extends AbstractAzureTest { public void testTouchFileStartWithDot() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, String.format(".%s.", new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureTouchFeature(session).touch(test, new TransferStatus()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testPreflightFilename() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final AzureTouchFeature feature = new AzureTouchFeature(session, null); + final AzureTouchFeature feature = new AzureTouchFeature(session); feature.preflight(container, new AsciiRandomStringService().random()); feature.preflight(container, new AlphanumericRandomStringService().random()); feature.preflight(container, String.format("%s.suffix", new AlphanumericRandomStringService().random())); diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java index 913e5121e8e..e13cd028989 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureUrlProviderTest.java @@ -1,9 +1,10 @@ package ch.cyberduck.core.azure; import ch.cyberduck.core.AlphanumericRandomStringService; -import ch.cyberduck.core.Credentials; import ch.cyberduck.core.DescriptiveUrl; +import ch.cyberduck.core.DescriptiveUrlBag; import ch.cyberduck.core.DisabledLoginCallback; +import ch.cyberduck.core.DisabledPasswordStore; import ch.cyberduck.core.Host; import ch.cyberduck.core.Path; import ch.cyberduck.core.features.Delete; @@ -17,6 +18,7 @@ import java.util.EnumSet; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; @Category(IntegrationTest.class) public class AzureUrlProviderTest extends AbstractAzureTest { @@ -24,21 +26,18 @@ public class AzureUrlProviderTest extends AbstractAzureTest { @Test public void testGet() throws Exception { final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path test = new Path(container, "f g", EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(test, new TransferStatus()); - assertEquals(5, new AzureUrlProvider(session).toUrl(test).filter(DescriptiveUrl.Type.signed).size()); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); - } - - @Test - public void testDisconnected() throws Exception { - final Host host = new Host(new AzureProtocol(), "kahy9boj3eib.blob.core.windows.net", new Credentials( - PROPERTIES.get("azure.user"), PROPERTIES.get("azure.key") - )); - final AzureSession session = new AzureSession(host); - final AzureUrlProvider provider = new AzureUrlProvider(session); - final Path container = new Path("test.cyberduck.ch", EnumSet.of(Path.Type.directory, Path.Type.volume)); - final Path file = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - assertEquals(DescriptiveUrl.EMPTY.getUrl(), provider.toUrl(file).find(DescriptiveUrl.Type.signed).getUrl()); + final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); + new AzureTouchFeature(session).touch(test, new TransferStatus()); + final DescriptiveUrlBag urls = new AzureUrlProvider(session, new DisabledPasswordStore() { + @Override + public String findLoginPassword(final Host bookmark) { + return PROPERTIES.get("azure.password"); + } + }).toUrl(test).filter(DescriptiveUrl.Type.signed); + assertEquals(5, urls.size()); + for(DescriptiveUrl url : urls) { + assertFalse(url.getUrl().isEmpty()); + } + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java index 4ae8e1faefe..6155eee9289 100644 --- a/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/azure/AzureWriteFeatureTest.java @@ -7,7 +7,9 @@ import ch.cyberduck.core.PathAttributes; import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.io.MD5ChecksumCompute; +import ch.cyberduck.core.io.StreamCancelation; import ch.cyberduck.core.io.StreamCopier; +import ch.cyberduck.core.io.StreamProgress; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.test.IntegrationTest; @@ -24,8 +26,7 @@ import java.util.EnumSet; import java.util.Map; -import com.microsoft.azure.storage.OperationContext; -import com.microsoft.azure.storage.blob.BlobType; +import com.azure.storage.blob.models.BlobType; import static org.junit.Assert.*; @@ -34,8 +35,6 @@ public class AzureWriteFeatureTest extends AbstractAzureTest { @Test public void testWriteOverrideAppendBlob() throws Exception { - final OperationContext context - = new OperationContext(); final TransferStatus status = new TransferStatus(); status.setMime("text/plain"); final byte[] content = RandomUtils.nextBytes(513); @@ -44,35 +43,33 @@ public void testWriteOverrideAppendBlob() throws Exception { status.setMetadata(Collections.singletonMap("Cache-Control", "public,max-age=86400")); final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final OutputStream out = new AzureWriteFeature(session, BlobType.APPEND_BLOB, context).write(test, status, new DisabledConnectionCallback()); + final OutputStream out = new AzureWriteFeature(session, BlobType.APPEND_BLOB).write(test, status, new DisabledConnectionCallback()); assertNotNull(out); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); - assertTrue(new AzureFindFeature(session, context).find(test)); - final PathAttributes attributes = new AzureAttributesFinderFeature(session, context).find(test); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out); + assertTrue(new AzureFindFeature(session).find(test)); + final PathAttributes attributes = new AzureAttributesFinderFeature(session).find(test); assertEquals(content.length, attributes.getSize()); - final Map metadata = new AzureMetadataFeature(session, context).getMetadata(test); + final Map metadata = new AzureMetadataFeature(session).getMetadata(test); assertEquals("text/plain", metadata.get("Content-Type")); assertEquals("public,max-age=86400", metadata.get("Cache-Control")); final byte[] buffer = new byte[content.length]; - final InputStream in = new AzureReadFeature(session, context).read(test, new TransferStatus(), new DisabledConnectionCallback()); + final InputStream in = new AzureReadFeature(session).read(test, new TransferStatus(), new DisabledConnectionCallback()); IOUtils.readFully(in, buffer); in.close(); assertArrayEquals(content, buffer); - final OutputStream overwrite = new AzureWriteFeature(session, context).write(test, new TransferStatus().setExists(true) + final OutputStream overwrite = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true) .setLength("overwrite".getBytes(StandardCharsets.UTF_8).length).setMetadata(Collections.singletonMap("Content-Type", "text/plain")), new DisabledConnectionCallback()); - new StreamCopier(new TransferStatus(), new TransferStatus()) + new StreamCopier(StreamCancelation.noop, StreamProgress.noop) .transfer(new ByteArrayInputStream("overwrite".getBytes(StandardCharsets.UTF_8)), overwrite); overwrite.close(); // Test double close overwrite.close(); - assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session, context).find(test).getSize()); - new AzureDeleteFeature(session, context).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session).find(test).getSize()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test public void testWriteOverrideBlockBlob() throws Exception { - final OperationContext context - = new OperationContext(); final TransferStatus status = new TransferStatus(); status.setMime("text/plain"); final byte[] content = RandomUtils.nextBytes(513); @@ -81,28 +78,28 @@ public void testWriteOverrideBlockBlob() throws Exception { status.setMetadata(Collections.singletonMap("Cache-Control", "public,max-age=86400")); final Path container = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path test = new Path(container, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - final OutputStream out = new AzureWriteFeature(session, BlobType.BLOCK_BLOB, context).write(test, status, new DisabledConnectionCallback()); + final OutputStream out = new AzureWriteFeature(session, BlobType.BLOCK_BLOB).write(test, status, new DisabledConnectionCallback()); assertNotNull(out); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out); - assertTrue(new AzureFindFeature(session, context).find(test)); - final PathAttributes attributes = new AzureAttributesFinderFeature(session, context).find(test); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), out); + assertTrue(new AzureFindFeature(session).find(test)); + final PathAttributes attributes = new AzureAttributesFinderFeature(session).find(test); assertEquals(content.length, attributes.getSize()); - final Map metadata = new AzureMetadataFeature(session, context).getMetadata(test); + final Map metadata = new AzureMetadataFeature(session).getMetadata(test); assertEquals("text/plain", metadata.get("Content-Type")); assertEquals("public,max-age=86400", metadata.get("Cache-Control")); final byte[] buffer = new byte[content.length]; - final InputStream in = new AzureReadFeature(session, context).read(test, new TransferStatus(), new DisabledConnectionCallback()); + final InputStream in = new AzureReadFeature(session).read(test, new TransferStatus(), new DisabledConnectionCallback()); IOUtils.readFully(in, buffer); in.close(); assertArrayEquals(content, buffer); - final OutputStream overwrite = new AzureWriteFeature(session, context).write(test, new TransferStatus().setExists(true) + final OutputStream overwrite = new AzureWriteFeature(session).write(test, new TransferStatus().setExists(true) .setLength("overwrite".getBytes(StandardCharsets.UTF_8).length).setMetadata(Collections.singletonMap("Content-Type", "text/plain")), new DisabledConnectionCallback()); - new StreamCopier(new TransferStatus(), new TransferStatus()) + new StreamCopier(StreamCancelation.noop, StreamProgress.noop) .transfer(new ByteArrayInputStream("overwrite".getBytes(StandardCharsets.UTF_8)), overwrite); overwrite.close(); // Test double close overwrite.close(); - assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session, context).find(test).getSize()); - new AzureDeleteFeature(session, context).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertEquals("overwrite".getBytes(StandardCharsets.UTF_8).length, new AzureAttributesFinderFeature(session).find(test).getSize()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java index 441bb645c9f..5f03c1f902d 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureDirectoryFeatureTest.java @@ -53,10 +53,11 @@ public void testMakeDirectoryEncrypted() throws Exception { new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir( + new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(test.getType().contains(Path.Type.placeholder)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -67,8 +68,9 @@ public void testMakeDirectoryLongFilenameEncrypted() throws Exception { new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); + final Path test = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir( + new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java index 559ba25e8e5..ea279109762 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureListServiceTest.java @@ -16,25 +16,17 @@ */ import ch.cyberduck.core.AlphanumericRandomStringService; -import ch.cyberduck.core.AttributedList; -import ch.cyberduck.core.Cache; -import ch.cyberduck.core.CachingFindFeature; import ch.cyberduck.core.DisabledListProgressListener; import ch.cyberduck.core.DisabledLoginCallback; import ch.cyberduck.core.DisabledPasswordCallback; import ch.cyberduck.core.Path; -import ch.cyberduck.core.PathCache; import ch.cyberduck.core.azure.AbstractAzureTest; import ch.cyberduck.core.azure.AzureDeleteFeature; -import ch.cyberduck.core.azure.AzureFindFeature; import ch.cyberduck.core.azure.AzureObjectListService; import ch.cyberduck.core.azure.AzureWriteFeature; -import ch.cyberduck.core.cryptomator.features.CryptoFindFeature; import ch.cyberduck.core.cryptomator.features.CryptoListService; import ch.cyberduck.core.cryptomator.features.CryptoTouchFeature; -import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.Delete; -import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.shared.DefaultTouchFeature; import ch.cyberduck.core.transfer.TransferStatus; import ch.cyberduck.core.vault.DefaultVaultRegistry; @@ -48,9 +40,9 @@ import java.util.Arrays; import java.util.EnumSet; -import java.util.Optional; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; @Category(IntegrationTest.class) @RunWith(value = Parameterized.class) @@ -64,40 +56,9 @@ public void testListCryptomator() throws Exception { final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final Path test = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - assertTrue(new CryptoListService(session, new AzureObjectListService(session, null), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); - new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new AzureWriteFeature(session, null)), new AzureWriteFeature(session, null), cryptomator).touch(test, new TransferStatus()); - assertEquals(test, new CryptoListService(session, new AzureObjectListService(session, null), cryptomator).list(vault, new DisabledListProgressListener() { - @Override - public void cleanup(final Path directory, final AttributedList list, final Optional e) { - assertEquals(vault, directory); - for(Path f : list) { - assertTrue(f.getType().contains(Path.Type.decrypted)); - } - } - - @Override - public void chunk(final Path directory, final AttributedList list) { - assertEquals(vault, directory); - for(Path f : list) { - assertTrue(f.getType().contains(Path.Type.decrypted)); - } - } - }).get(0)); - { - final Cache cache = new PathCache(1); - assertTrue(new CachingFindFeature(session, cache, new CryptoFindFeature(session, new AzureFindFeature(session, null), cryptomator)).find(test)); - assertFalse(cache.isCached(vault)); - } - { - final Cache cache = new PathCache(1); - assertTrue(new CachingFindFeature(session, cache, new CryptoFindFeature(session, new DefaultFindFeature(session), cryptomator)).find(test)); - assertTrue(cache.isCached(vault)); - final AttributedList list = cache.get(vault); - assertFalse(list.isEmpty()); - for(Path f : list) { - assertTrue(f.getType().contains(Path.Type.decrypted)); - } - } - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertTrue(new CryptoListService(session, new AzureObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).isEmpty()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new AzureWriteFeature(session)), new AzureWriteFeature(session), cryptomator).touch(test, new TransferStatus()); + assertEquals(test, new CryptoListService(session, new AzureObjectListService(session), cryptomator).list(vault, new DisabledListProgressListener()).get(0)); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java index 7ddc3603075..f6103e1a155 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureMoveFeatureTest.java @@ -60,25 +60,25 @@ public void testMove() throws Exception { new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final Path folder = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir( + final Path folder = cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir( new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)), new TransferStatus()); - final Path file = new CryptoTouchFeature(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch( + final Path file = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch( new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)), new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); - final Move move = cryptomator.getFeature(session, Move.class, new AzureMoveFeature(session, null)); + final Move move = cryptomator.getFeature(session, Move.class, new AzureMoveFeature(session)); // rename file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); move.move(file, fileRenamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); - assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(file)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(fileRenamed)); + assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(file)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(fileRenamed)); // rename folder final Path folderRenamed = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.placeholder)); move.move(folder, folderRenamed, new TransferStatus(), new Delete.DisabledCallback(), new DisabledConnectionCallback()); - assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folder)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folderRenamed)); + assertFalse(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folder)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folderRenamed)); final Path fileRenamedInRenamedFolder = new Path(folderRenamed, "f1", EnumSet.of(Path.Type.file)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(fileRenamedInRenamedFolder)); - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList( + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(fileRenamedInRenamedFolder)); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList( fileRenamedInRenamedFolder, folderRenamed, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java index 2a00e512e70..972d952dd29 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureTouchFeatureTest.java @@ -58,12 +58,12 @@ public void testTouchLongFilenameEncrypted() throws Exception { final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch( + final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch( new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize()); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(test)); - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(test)); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } @Test @@ -75,11 +75,11 @@ public void testTouchLongFilenameEncryptedDefaultFeature() throws Exception { final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); final TransferStatus status = new TransferStatus(); - final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch( + final Path test = new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch( new Path(vault, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)), status); assertEquals(TransferStatus.UNKNOWN_LENGTH, test.attributes().getSize()); assertEquals(TransferStatus.UNKNOWN_LENGTH, status.getResponse().getSize()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(test)); - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java index 910c30a75dc..a11d3355b97 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/AzureWriteFeatureTest.java @@ -54,8 +54,6 @@ import java.util.Arrays; import java.util.EnumSet; -import com.microsoft.azure.storage.OperationContext; - import static org.junit.Assert.*; @Category(IntegrationTest.class) @@ -72,7 +70,7 @@ public void testWrite() throws Exception { new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); session.withRegistry(new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator)); - final CryptoWriteFeature writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session, null), cryptomator); + final CryptoWriteFeature writer = new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator); final FileHeader header = cryptomator.getFileHeaderCryptor().create(); status.setHeader(cryptomator.getFileHeaderCryptor().encryptHeader(header)); status.setNonces(new RotatingNonceGenerator(cryptomator.getNonceSize(), cryptomator.numberOfChunks(content.length))); @@ -82,14 +80,13 @@ public void testWrite() throws Exception { assertNotNull(out); new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out); out.close(); - final OperationContext context = new OperationContext(); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(test)); - final PathAttributes attributes = new CryptoListService(session, new AzureListService(session, context), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes(); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(test)); + final PathAttributes attributes = new CryptoListService(session, new AzureListService(session), cryptomator).list(test.getParent(), new DisabledListProgressListener()).get(test).attributes(); assertEquals(content.length, attributes.getSize()); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length); - final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session, context), cryptomator).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(test, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); new StreamCopier(status, status).transfer(in, buffer); assertArrayEquals(content, buffer.toByteArray()); - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, context)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(test, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); } } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java index 5c63dea021f..b3150c7c79e 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CopyWorkerTest.java @@ -35,7 +35,9 @@ import ch.cyberduck.core.cryptomator.features.CryptoWriteFeature; import ch.cyberduck.core.features.Directory; import ch.cyberduck.core.features.Find; +import ch.cyberduck.core.io.StreamCancelation; import ch.cyberduck.core.io.StreamCopier; +import ch.cyberduck.core.io.StreamProgress; import ch.cyberduck.core.pool.SessionPool; import ch.cyberduck.core.shared.DefaultFindFeature; import ch.cyberduck.core.shared.DefaultTouchFeature; @@ -63,7 +65,6 @@ import java.util.EnumSet; import static org.junit.Assert.*; -import static org.junit.Assume.assumeTrue; @Category(IntegrationTest.class) @RunWith(value = Parameterized.class) @@ -81,15 +82,15 @@ public void testCopyFile() throws Exception { session.withRegistry(registry); final byte[] content = RandomUtils.nextBytes(40500); final TransferStatus status = new TransferStatus(); - new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new AzureDeleteFeature(session, null), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), new CryptoWriteFeature<>(session, new AzureWriteFeature(session, null), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); + new CryptoBulkFeature<>(session, new DisabledBulkFeature(), new AzureDeleteFeature(session), cryptomator).pre(Transfer.Type.upload, Collections.singletonMap(new TransferItem(source), status), new DisabledConnectionCallback()); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), new CryptoWriteFeature<>(session, new AzureWriteFeature(session), cryptomator).write(source, status.setLength(content.length), new DisabledConnectionCallback())); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(source)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(target)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(source)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(target)); final ByteArrayOutputStream out = new ByteArrayOutputStream(content.length); - assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(target, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out)); + assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(target, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out)); assertArrayEquals(content, out.toByteArray()); new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session); } @@ -105,9 +106,9 @@ public void testCopyToDifferentFolderCryptomator() throws Exception { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature(session, new DefaultTouchFeature(new AzureWriteFeature(session, null)), new AzureWriteFeature(session, null), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new DefaultTouchFeature<>(new AzureWriteFeature(session)), new AzureWriteFeature(session), cryptomator).touch(source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); @@ -119,7 +120,6 @@ public void testCopyToDifferentFolderCryptomator() throws Exception { @Test public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception { - assumeTrue(vaultVersion == CryptoVault.VAULT_VERSION_DEPRECATED); final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final CryptoVault cryptomator = new CryptoVault(new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory))); final Path vault = cryptomator.create(session, new VaultCredentials("test"), vaultVersion); @@ -128,9 +128,9 @@ public void testCopyToDifferentFolderLongFilenameCryptomator() throws Exception final Path target = new Path(targetFolder, new AlphanumericRandomStringService(130).random(), EnumSet.of(Path.Type.file)); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - new CryptoTouchFeature(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(source, new TransferStatus()); + new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch(source, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(source)); - cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(targetFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(targetFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(targetFolder)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); @@ -149,20 +149,20 @@ public void testCopyFolder() throws Exception { final Path file = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(folder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(folder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(folder)); - new CryptoTouchFeature(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(file, new TransferStatus()); + new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch(file, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(file)); // copy file final Path fileRenamed = new Path(folder, "f1", EnumSet.of(Path.Type.file)); new CopyWorker(Collections.singletonMap(file, fileRenamed), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()).run(session); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(file)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(fileRenamed)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(file)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(fileRenamed)); // copy folder final Path folderRenamed = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); new CopyWorker(Collections.singletonMap(folder, folderRenamed), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()).run(session); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folder)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(folderRenamed)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folder)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(folderRenamed)); final Path fileRenamedInRenamedFolder = new Path(folderRenamed, "f1", EnumSet.of(Path.Type.file)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(fileRenamedInRenamedFolder)); registry.clear(); @@ -175,23 +175,23 @@ public void testCopyFileIntoVault() throws Exception { final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path cleartextFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final byte[] content = RandomUtils.nextBytes(40500); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), new AzureWriteFeature(session, null).write(cleartextFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback())); - assertTrue(new AzureFindFeature(session, null).find(cleartextFile)); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(new ByteArrayInputStream(content), new AzureWriteFeature(session).write(cleartextFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback())); + assertTrue(new AzureFindFeature(session).find(cleartextFile)); final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); // copy file into vault final CopyWorker worker = new CopyWorker(Collections.singletonMap(cleartextFile, encryptedFile), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(new AzureFindFeature(session, null).find(cleartextFile)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFile)); + assertTrue(new AzureFindFeature(session).find(cleartextFile)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFile)); final ByteArrayOutputStream out = new ByteArrayOutputStream(content.length); - assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(encryptedFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out)); + assertEquals(content.length, IOUtils.copy(new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(encryptedFile, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()), out)); assertArrayEquals(content, out.toByteArray()); registry.clear(); new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session); @@ -203,10 +203,10 @@ public void testCopyDirectoryIntoVault() throws Exception { final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path cleartextFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path cleartextFile = new Path(cleartextFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureDirectoryFeature(session, null).mkdir(cleartextFolder, new TransferStatus()); - new AzureTouchFeature(session, null).touch(cleartextFile, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(cleartextFolder)); - assertTrue(new AzureFindFeature(session, null).find(cleartextFile)); + new AzureDirectoryFeature(session).mkdir(cleartextFolder, new TransferStatus()); + new AzureTouchFeature(session).touch(cleartextFile, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(cleartextFolder)); + assertTrue(new AzureFindFeature(session).find(cleartextFile)); final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); @@ -218,8 +218,8 @@ public void testCopyDirectoryIntoVault() throws Exception { worker.run(session); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); - assertTrue(new AzureFindFeature(session, null).find(cleartextFolder)); - assertTrue(new AzureFindFeature(session, null).find(cleartextFile)); + assertTrue(new AzureFindFeature(session).find(cleartextFolder)); + assertTrue(new AzureFindFeature(session).find(cleartextFile)); registry.clear(); new DeleteWorker(new DisabledLoginCallback(), Collections.singletonList(vault), new DisabledProgressListener()).run(session); } @@ -229,23 +229,23 @@ public void testCopyFileOutsideVault() throws Exception { final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path vault = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path clearFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); - new AzureDirectoryFeature(session, null).mkdir(clearFolder, new TransferStatus()); + new AzureDirectoryFeature(session).mkdir(clearFolder, new TransferStatus()); final Path encryptedFolder = new Path(vault, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path encryptedFile = new Path(encryptedFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CryptoVault cryptomator = new CryptoVault(vault); cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // move file outside vault final Path cleartextFile = new Path(clearFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(encryptedFile, cleartextFile), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFile)); - assertTrue(new AzureFindFeature(session, null).find(cleartextFile)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFile)); + assertTrue(new AzureFindFeature(session).find(cleartextFile)); registry.clear(); new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(vault, clearFolder), new DisabledProgressListener()).run(session); } @@ -260,19 +260,19 @@ public void testCopyDirectoryOutsideVault() throws Exception { cryptomator.create(session, new VaultCredentials("test"), vaultVersion); final DefaultVaultRegistry registry = new DefaultVaultRegistry(new DisabledPasswordCallback(), cryptomator); session.withRegistry(registry); - cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session, null)).mkdir(encryptedFolder, new TransferStatus()); + cryptomator.getFeature(session, Directory.class, new AzureDirectoryFeature(session)).mkdir(encryptedFolder, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFolder)); - new CryptoTouchFeature(session, new AzureTouchFeature(session, null), new AzureWriteFeature(session, null), cryptomator).touch(encryptedFile, new TransferStatus()); + new CryptoTouchFeature<>(session, new AzureTouchFeature(session), new AzureWriteFeature(session), cryptomator).touch(encryptedFile, new TransferStatus()); assertTrue(cryptomator.getFeature(session, Find.class, new DefaultFindFeature(session)).find(encryptedFile)); // copy directory outside vault final Path cleartextFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(encryptedFolder, cleartextFolder), new SessionPool.SingleSessionPool(session, registry), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFolder)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(encryptedFile)); - assertTrue(new AzureFindFeature(session, null).find(cleartextFolder)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFolder)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(encryptedFile)); + assertTrue(new AzureFindFeature(session).find(cleartextFolder)); final Path fileRenamed = new Path(cleartextFolder, encryptedFile.getName(), EnumSet.of(Path.Type.file)); - assertTrue(new AzureFindFeature(session, null).find(fileRenamed)); + assertTrue(new AzureFindFeature(session).find(fileRenamed)); registry.clear(); new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(cleartextFolder, vault), new DisabledProgressListener()).run(session); } diff --git a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java index 22c656ca57b..4ac361ed8c5 100644 --- a/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/cryptomator/CryptoAzureSingleTransferWorkerTest.java @@ -35,7 +35,9 @@ import ch.cyberduck.core.features.Delete; import ch.cyberduck.core.features.Find; import ch.cyberduck.core.io.DisabledStreamListener; +import ch.cyberduck.core.io.StreamCancelation; import ch.cyberduck.core.io.StreamCopier; +import ch.cyberduck.core.io.StreamProgress; import ch.cyberduck.core.local.DefaultLocalDirectoryFeature; import ch.cyberduck.core.notification.DisabledNotificationService; import ch.cyberduck.core.transfer.DisabledTransferErrorCallback; @@ -98,26 +100,23 @@ public void testUpload() throws Exception { public TransferAction prompt(final TransferItem file) { return TransferAction.overwrite; } - }, new DisabledTransferErrorCallback(), - new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()) { - - }.run(session)); - assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session, null)).find(dir1)); - assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session, null)).find(file1).getSize()); + }, new DisabledTransferErrorCallback(), new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()).run(session)); + assertTrue(cryptomator.getFeature(session, Find.class, new AzureFindFeature(session)).find(dir1)); + assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session)).find(file1).getSize()); { final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length); - final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(in, buffer); + final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(in, buffer); assertArrayEquals(content, buffer.toByteArray()); } - assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session, null)).find(file2).getSize()); + assertEquals(content.length, cryptomator.getFeature(session, AttributesFinder.class, new AzureAttributesFinderFeature(session)).find(file2).getSize()); { final ByteArrayOutputStream buffer = new ByteArrayOutputStream(content.length); - final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session, null), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); - new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(in, buffer); + final InputStream in = new CryptoReadFeature(session, new AzureReadFeature(session), cryptomator).read(file1, new TransferStatus().setLength(content.length), new DisabledConnectionCallback()); + new StreamCopier(StreamCancelation.noop, StreamProgress.noop).transfer(in, buffer); assertArrayEquals(content, buffer.toByteArray()); } - cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session, null)).delete(Arrays.asList(file1, file2, dir1, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); + cryptomator.getFeature(session, Delete.class, new AzureDeleteFeature(session)).delete(Arrays.asList(file1, file2, dir1, vault), new DisabledLoginCallback(), new Delete.DisabledCallback()); localFile1.delete(); localFile2.delete(); localDirectory1.delete(); diff --git a/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java index 2860fc5dd8f..78cdd6bf7d4 100644 --- a/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/worker/AzureSingleTransferWorkerTest.java @@ -67,14 +67,13 @@ public void testDownload() throws Exception { { final byte[] content = RandomUtils.nextBytes(39864); final TransferStatus writeStatus = new TransferStatus().setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus())); - final StatusOutputStream out = new AzureWriteFeature(session, null).write(test, writeStatus, new DisabledConnectionCallback()); + final StatusOutputStream out = new AzureWriteFeature(session).write(test, writeStatus, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(writeStatus, writeStatus).withLimit((long) content.length).transfer(new ByteArrayInputStream(content), out); - out.close(); } final byte[] content = RandomUtils.nextBytes(39864); - final TransferStatus writeStatus = new TransferStatus().setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus())); - final StatusOutputStream out = new AzureWriteFeature(session, null).write(test, writeStatus, new DisabledConnectionCallback()); + final TransferStatus writeStatus = new TransferStatus().setExists(true).setLength(content.length).setChecksum(new SHA256ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus())); + final StatusOutputStream out = new AzureWriteFeature(session).write(test, writeStatus, new DisabledConnectionCallback()); assertNotNull(out); new StreamCopier(writeStatus, writeStatus).withLimit((long) content.length).transfer(new ByteArrayInputStream(content), out); out.close(); @@ -85,11 +84,9 @@ public TransferAction prompt(final TransferItem file) { return TransferAction.overwrite; } }, new DisabledTransferErrorCallback(), - new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()) { - - }.run(session)); + new DisabledProgressListener(), new DisabledStreamListener(), new DisabledLoginCallback(), new DisabledNotificationService()).run(session)); assertArrayEquals(content, IOUtils.toByteArray(localFile.getInputStream())); - new AzureDeleteFeature(session, null).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); + new AzureDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback()); localFile.delete(); } } diff --git a/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java b/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java index 7f077ec46bd..6fd3d271519 100644 --- a/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java +++ b/azure/src/test/java/ch/cyberduck/core/worker/CopyWorkerTest.java @@ -46,12 +46,12 @@ public void testCopyFile() throws Exception { final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path source = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); final Path target = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(source, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(source)); + new AzureTouchFeature(session).touch(source, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(source)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(source, target), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(new AzureFindFeature(session, null).find(source)); - assertTrue(new AzureFindFeature(session, null).find(target)); + assertTrue(new AzureFindFeature(session).find(source)); + assertTrue(new AzureFindFeature(session).find(target)); new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(source, target), new DisabledProgressListener()).run(session); } @@ -59,17 +59,17 @@ public void testCopyFile() throws Exception { public void testCopyFileToDirectory() throws Exception { final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path sourceFile = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureTouchFeature(session, null).touch(sourceFile, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(sourceFile)); + new AzureTouchFeature(session).touch(sourceFile, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(sourceFile)); final Path targetFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path targetFile = new Path(targetFolder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureDirectoryFeature(session, null).mkdir(targetFolder, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(targetFolder)); + new AzureDirectoryFeature(session).mkdir(targetFolder, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(targetFolder)); // copy file into vault final CopyWorker worker = new CopyWorker(Collections.singletonMap(sourceFile, targetFile), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(new AzureFindFeature(session, null).find(sourceFile)); - assertTrue(new AzureFindFeature(session, null).find(targetFile)); + assertTrue(new AzureFindFeature(session).find(sourceFile)); + assertTrue(new AzureFindFeature(session).find(targetFile)); new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(sourceFile, targetFolder), new DisabledProgressListener()).run(session); } @@ -78,19 +78,19 @@ public void testCopyDirectory() throws Exception { final Path home = new Path("cyberduck", EnumSet.of(Path.Type.directory, Path.Type.volume)); final Path folder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path sourceFile = new Path(folder, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.file)); - new AzureDirectoryFeature(session, null).mkdir(folder, new TransferStatus()); - new AzureTouchFeature(session, null).touch(sourceFile, new TransferStatus()); - assertTrue(new AzureFindFeature(session, null).find(folder)); - assertTrue(new AzureFindFeature(session, null).find(sourceFile)); + new AzureDirectoryFeature(session).mkdir(folder, new TransferStatus()); + new AzureTouchFeature(session).touch(sourceFile, new TransferStatus()); + assertTrue(new AzureFindFeature(session).find(folder)); + assertTrue(new AzureFindFeature(session).find(sourceFile)); // move directory into vault final Path targetFolder = new Path(home, new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory)); final Path targetFile = new Path(targetFolder, sourceFile.getName(), EnumSet.of(Path.Type.file)); final CopyWorker worker = new CopyWorker(Collections.singletonMap(folder, targetFolder), new SessionPool.SingleSessionPool(session), PathCache.empty(), new DisabledProgressListener(), new DisabledConnectionCallback()); worker.run(session); - assertTrue(new AzureFindFeature(session, null).find(targetFolder)); - assertTrue(new AzureFindFeature(session, null).find(targetFile)); - assertTrue(new AzureFindFeature(session, null).find(folder)); - assertTrue(new AzureFindFeature(session, null).find(sourceFile)); + assertTrue(new AzureFindFeature(session).find(targetFolder)); + assertTrue(new AzureFindFeature(session).find(targetFile)); + assertTrue(new AzureFindFeature(session).find(folder)); + assertTrue(new AzureFindFeature(session).find(sourceFile)); new DeleteWorker(new DisabledLoginCallback(), Arrays.asList(folder, targetFolder), new DisabledProgressListener()).run(session); } } From 1279bc9765e50efb58c76f424d7c9283ccf36ac4 Mon Sep 17 00:00:00 2001 From: David Kocher Date: Tue, 15 Jul 2025 12:27:50 +0200 Subject: [PATCH 2/3] Update dependency. --- azure/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure/pom.xml b/azure/pom.xml index ac3a23dfcba..29d2e67afd3 100644 --- a/azure/pom.xml +++ b/azure/pom.xml @@ -22,7 +22,7 @@ azure - 12.26.1 + 12.30.0 From 7d42f5942099baa3ea4293ffd3212fcb3d3986ab Mon Sep 17 00:00:00 2001 From: David Kocher Date: Sun, 20 Jul 2025 09:00:13 +0200 Subject: [PATCH 3/3] Workaround credentials validation. --- .../ch/cyberduck/core/azure/AzureSession.java | 86 ++++++++++--------- 1 file changed, 45 insertions(+), 41 deletions(-) diff --git a/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java b/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java index dde9e74192c..b6a4873aea9 100644 --- a/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java +++ b/azure/src/main/java/ch/cyberduck/core/azure/AzureSession.java @@ -18,10 +18,10 @@ import ch.cyberduck.core.Credentials; import ch.cyberduck.core.Host; import ch.cyberduck.core.HostKeyCallback; +import ch.cyberduck.core.HostUrlProvider; import ch.cyberduck.core.ListService; import ch.cyberduck.core.LoginCallback; import ch.cyberduck.core.PreferencesUseragentProvider; -import ch.cyberduck.core.Scheme; import ch.cyberduck.core.azure.apache.ApacheHttpClient; import ch.cyberduck.core.exception.BackgroundException; import ch.cyberduck.core.features.*; @@ -38,31 +38,26 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; import java.util.Collections; -import java.util.List; import com.azure.core.credential.AzureSasCredential; import com.azure.core.exception.HttpResponseException; -import com.azure.core.http.HttpPipeline; +import com.azure.core.http.HttpHeaderName; import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.HttpPipelineCallContext; import com.azure.core.http.HttpPipelineNextPolicy; +import com.azure.core.http.HttpPipelineNextSyncPolicy; import com.azure.core.http.HttpResponse; import com.azure.core.http.policy.AddDatePolicy; import com.azure.core.http.policy.AddHeadersPolicy; import com.azure.core.http.policy.AzureSasCredentialPolicy; +import com.azure.core.http.policy.BearerTokenAuthenticationPolicy; import com.azure.core.http.policy.HttpPipelinePolicy; import com.azure.core.http.policy.RequestIdPolicy; -import com.azure.storage.blob.BlobServiceAsyncClient; +import com.azure.core.http.policy.UserAgentPolicy; import com.azure.storage.blob.BlobServiceClient; -import com.azure.storage.blob.BlobServiceVersion; -import com.azure.storage.blob.implementation.models.EncryptionScope; +import com.azure.storage.blob.BlobServiceClientBuilder; import com.azure.storage.blob.models.AccountKind; -import com.azure.storage.blob.models.BlobContainerEncryptionScope; -import com.azure.storage.blob.models.CpkInfo; import com.azure.storage.common.StorageSharedKeyCredential; import com.azure.storage.common.implementation.Constants; import com.azure.storage.common.policy.MetadataValidationPolicy; @@ -87,37 +82,30 @@ public AzureSession(final Host h, final X509TrustManager trust, final X509KeyMan } @Override - protected BlobServiceClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) throws BackgroundException { + protected BlobServiceClient connect(final ProxyFinder proxy, final HostKeyCallback key, final LoginCallback prompt, final CancelCallback cancel) { final HttpClientBuilder pool = builder.build(proxy, this, prompt); - final List policies = new ArrayList<>(); - policies.add(new RequestIdPolicy()); - policies.add(new RequestRetryPolicy(new RequestRetryOptions())); - policies.add(new AddDatePolicy()); - policies.add(new AddHeadersPolicy(new com.azure.core.http.HttpHeaders( - Collections.singletonMap(HttpHeaders.USER_AGENT, new PreferencesUseragentProvider().get())) - )); - policies.add(new MetadataValidationPolicy()); - policies.add(authenticator); - policies.add(new ResponseValidationPolicyBuilder() - .addOptionalEcho(Constants.HeaderConstants.CLIENT_REQUEST_ID) - .addOptionalEcho(Constants.HeaderConstants.ENCRYPTION_KEY_SHA256) - .build()); - final HttpPipeline pipeline = new HttpPipelineBuilder() - .httpClient(new ApacheHttpClient(pool)) - .policies(policies.toArray(new HttpPipelinePolicy[0])) - .build(); - try { - final Constructor blobServiceAsyncClientConstructor = BlobServiceAsyncClient.class.getDeclaredConstructor(HttpPipeline.class, String.class, BlobServiceVersion.class, String.class, CpkInfo.class, EncryptionScope.class, BlobContainerEncryptionScope.class, boolean.class); - blobServiceAsyncClientConstructor.setAccessible(true); - final BlobServiceAsyncClient serviceAsyncClient = blobServiceAsyncClientConstructor.newInstance(pipeline, String.format("%s://%s", Scheme.https, host.getHostname()), - BlobServiceVersion.getLatest(), null, null, null, null, false); - final Constructor blobServiceClientConstructor = BlobServiceClient.class.getDeclaredConstructor(BlobServiceAsyncClient.class); - blobServiceClientConstructor.setAccessible(true); - return blobServiceClientConstructor.newInstance(serviceAsyncClient); - } - catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - throw new BackgroundException(e); - } + return new BlobServiceClientBuilder() + .endpoint(new HostUrlProvider().withUsername(false).get(host)) + .pipeline(new HttpPipelineBuilder() + .httpClient(new ApacheHttpClient(pool)) + .policies( + new EmptyAuthenticationPolicy(), + new UserAgentPolicy(new PreferencesUseragentProvider().get()), + new RequestIdPolicy(), + new RequestRetryPolicy(new RequestRetryOptions()), + new AddDatePolicy(), + new AddHeadersPolicy(new com.azure.core.http.HttpHeaders( + Collections.singletonMap(HttpHeaders.USER_AGENT, new PreferencesUseragentProvider().get())) + ), + new MetadataValidationPolicy(), + authenticator, + new ResponseValidationPolicyBuilder() + .addOptionalEcho(HttpHeaderName.fromString(Constants.HeaderConstants.CLIENT_REQUEST_ID)) + .addOptionalEcho(HttpHeaderName.fromString(Constants.HeaderConstants.ENCRYPTION_KEY_SHA256)) + .build() + ) + .build()) + .buildClient(); } private static final class CredentialsHttpPipelinePolicy implements HttpPipelinePolicy { @@ -211,4 +199,20 @@ public T _getFeature(final Class type) { } return super._getFeature(type); } + + private static final class EmptyAuthenticationPolicy extends BearerTokenAuthenticationPolicy { + public EmptyAuthenticationPolicy() { + super(request -> Mono.empty()); + } + + @Override + public HttpResponse processSync(final HttpPipelineCallContext context, final HttpPipelineNextSyncPolicy next) { + return next.processSync(); + } + + @Override + public Mono process(final HttpPipelineCallContext context, final HttpPipelineNextPolicy next) { + return next.process(); + } + } }