Skip to content

Update library dependency for Azure implementation #17259

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions azure/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</parent>
<artifactId>azure</artifactId>
<properties>
<azure-storage-version>8.6.6</azure-storage-version>
<azure-storage-version>12.30.0</azure-storage-version>
</properties>

<dependencies>
Expand All @@ -39,8 +39,8 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>${azure-storage-version}</version>
<exclusions>
<exclusion>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,71 +1,61 @@
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;
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.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:
* <p/>
* 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.
* <p/>
* 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.
* <p/>
* 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
Expand All @@ -76,7 +66,7 @@ public List<Acl.Role> getAvailableAclRoles(final List<Path> files) {

@Override
public List<Acl.User> getAvailableAclUsers() {
return new ArrayList<Acl.User>(Collections.singletonList(
return new ArrayList<>(Collections.singletonList(
new Acl.GroupUser(Acl.GroupUser.EVERYONE, false))
);
}
Expand All @@ -85,22 +75,21 @@ public List<Acl.User> 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);
}
}
Expand All @@ -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);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<CloudBlob> {
public class AzureAttributesFinderFeature implements AttributesFinder, AttributesAdapter<BlobItemProperties> {
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
Expand All @@ -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
Expand All @@ -98,37 +87,45 @@ 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) {
// Found common prefix
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<String, String> 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;
}
}
Loading