Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
1 change: 1 addition & 0 deletions sdk/storage/azure-storage-blob-nio/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Release History

## 12.0.0-beta.3 (Unreleased)
- Added support for FileSystemProvider.checkAccess method
- Added support for file key on AzureBasicFileAttributes and AzureBlobFileAttributes

## 12.0.0-beta.2 (2020-08-13)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public final class AzureFileSystem extends FileSystem {
this.fileStores = this.initializeFileStores(config);
} catch (RuntimeException e) {
throw LoggingUtility.logError(logger, new IllegalArgumentException("There was an error parsing the "
+ "configurations map. Please ensure all fields are set to a legal value of the correct type."));
+ "configurations map. Please ensure all fields are set to a legal value of the correct type.", e));
} catch (IOException e) {
throw LoggingUtility.logError(logger,
new IOException("Initializing FileStores failed. FileSystem could not be opened.", e));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.net.HttpURLConnection;
import java.net.URI;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.AccessDeniedException;
import java.nio.file.AccessMode;
import java.nio.file.CopyOption;
import java.nio.file.DirectoryNotEmptyException;
Expand Down Expand Up @@ -373,7 +374,8 @@ public OutputStream newOutputStream(Path path, OpenOption... options) throws IOE
// Write and truncate must be specified
if (!optionsList.contains(StandardOpenOption.WRITE)
|| !optionsList.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
throw new IllegalArgumentException("Write and TruncateExisting must be specified to open an OutputStream");
throw LoggingUtility.logError(logger,
Comment thread
rickle-msft marked this conversation as resolved.
new IllegalArgumentException("Write and TruncateExisting must be specified to open an OutputStream"));
}

AzureResource resource = new AzureResource(path);
Expand Down Expand Up @@ -785,11 +787,14 @@ public FileStore getFileStore(Path path) throws IOException {
}

/**
* Unsupported.
* Checks the existence, and optionally the accessibility, of a file.
* <p>
* This method may only be used to check the existence of a file. It is not possible to determine the permissions
* granted to a given client, so if any mode argument is specified, an {@link UnsupportedOperationException} will be

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be UnsupportedOperationException or AccessDeniedException?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AccessDenied

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(per the jdk docs for this method: "the requested access would be denied or the access cannot be determined because the Java virtual machine has insufficient privileges or other reasons." I think it's reasonable to put this under access cannot be determined)

* thrown.
*
* @param path path
* @param accessModes accessMode
* @throws UnsupportedOperationException Operation is not supported.
* @param path the path to the file to check
* @param accessModes The access modes to check; may have zero elements
* @throws NoSuchFileException if a file does not exist
* @throws java.nio.file.AccessDeniedException the requested access would be denied or the access cannot be
* determined because the Java virtual machine has insufficient privileges or other reasons
Expand All @@ -798,7 +803,23 @@ public FileStore getFileStore(Path path) throws IOException {
*/
@Override
public void checkAccess(Path path, AccessMode... accessModes) throws IOException {
throw new UnsupportedOperationException();
if (accessModes != null && accessModes.length != 0) {
throw LoggingUtility.logError(logger, new AccessDeniedException("The access cannot be determined."));
}
AzurePath.ensureFileSystemOpen(path);

// Read attributes already wraps BlobStorageException in an IOException.
try {
readAttributes(path, BasicFileAttributes.class);
} catch (IOException e) {
Throwable cause = e.getCause();
if (cause instanceof BlobStorageException
&& BlobErrorCode.BLOB_NOT_FOUND.equals(((BlobStorageException) cause).getErrorCode())) {
throw LoggingUtility.logError(logger, new NoSuchFileException(path.toString()));
} else {
throw e;
Comment thread
rickle-msft marked this conversation as resolved.
Outdated
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ class APISpec extends Specification {

interceptorManager.close()
}

static Mono<ByteBuffer> collectBytesInBuffer(Flux<ByteBuffer> content) {
return FluxUtil.collectBytesInByteBufferStream(content).map { bytes -> ByteBuffer.wrap(bytes) }
}
Expand Down Expand Up @@ -372,13 +372,17 @@ class APISpec extends Specification {
}
}

Map<String, Object> initializeConfigMap() {
Map<String, Object> initializeConfigMap(HttpPipelinePolicy... policies) {
def config = [:]
config[AzureFileSystem.AZURE_STORAGE_HTTP_CLIENT] = getHttpClient()
def policyList = []
Comment thread
rickle-msft marked this conversation as resolved.
for (HttpPipelinePolicy policy : policies) {
policyList.push(policy)
}
if (testMode == TestMode.RECORD) {
config[AzureFileSystem.AZURE_STORAGE_HTTP_POLICIES] =
[interceptorManager.getRecordPolicy()] as HttpPipelinePolicy[]
policyList.push(interceptorManager.getRecordPolicy())
}
config[AzureFileSystem.AZURE_STORAGE_HTTP_POLICIES] = policyList as HttpPipelinePolicy[]
config[AzureFileSystem.AZURE_STORAGE_USE_HTTPS] = defaultEndpointTemplate.startsWith("https")
return config as Map<String, Object>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,28 @@

package com.azure.storage.blob.nio

import com.azure.core.http.HttpHeaders
import com.azure.core.http.HttpMethod
import com.azure.core.http.HttpPipelineCallContext
import com.azure.core.http.HttpPipelineNextPolicy
import com.azure.core.http.HttpRequest
import com.azure.core.http.HttpResponse
import com.azure.core.http.policy.HttpPipelinePolicy
import com.azure.storage.blob.BlobClient
import com.azure.storage.blob.models.AccessTier
import com.azure.storage.blob.models.BlobErrorCode
import com.azure.storage.blob.models.BlobHttpHeaders
import com.azure.storage.blob.specialized.AppendBlobClient
import com.azure.storage.blob.specialized.BlockBlobClient
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import spock.lang.Requires
import spock.lang.Unroll

import java.nio.ByteBuffer
import java.nio.charset.Charset
import java.nio.file.AccessDeniedException
import java.nio.file.AccessMode
import java.nio.file.DirectoryNotEmptyException
import java.nio.file.FileAlreadyExistsException
import java.nio.file.FileSystem
Expand Down Expand Up @@ -1058,6 +1071,140 @@ class AzureFileSystemProviderTest extends APISpec {
thrown(IOException)
}

def "CheckAccess"() {
setup:
def fs = createFS(config)
def path = fs.getPath(generateBlobName())
def os = fs.provider().newOutputStream(path)
os.close()

when:
fs.provider().checkAccess(path)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could probably just do

expect:
fs.provider().checkAccess(path)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's functionally the same. If all I'm doing is check the call succeeds, I usually like to make that explicit by saying I don't want an exception.


then:
notThrown(Exception)
}

@Unroll
def "CheckAccess AccessDenied"() {
setup:
def fs = createFS(config)
def path = fs.getPath(generateBlobName())
def os = fs.provider().newOutputStream(path)
os.close()

when:
fs.provider().checkAccess(path, mode)

then:
thrown(AccessDeniedException)

where:
mode | _
AccessMode.READ | _
AccessMode.WRITE | _
AccessMode.EXECUTE | _
}

def "CheckAccess IOException"() {
setup:
config = initializeConfigMap(new CheckAccessIoExceptionPolicy())
def fs = createFS(config)
def path = fs.getPath(generateBlobName())
def os = fs.provider().newOutputStream(path)
os.close()

when:
fs.provider().checkAccess(path)

then:
def e = thrown(IOException)
!(e instanceof NoSuchFileException)
}

class CheckAccessIoExceptionPolicy implements HttpPipelinePolicy {
@Override
Mono<HttpResponse> process(HttpPipelineCallContext httpPipelineCallContext, HttpPipelineNextPolicy httpPipelineNextPolicy) {
HttpRequest request = httpPipelineCallContext.getHttpRequest()
// GetProperties call to blob
if (request.getUrl().getPath().split("/").size() == 3 && request.getHttpMethod() == (HttpMethod.HEAD)) {
return Mono.just(new checkAccessIoExceptionResponse(request))
} else {
return httpPipelineNextPolicy.process()
}
}
}

class checkAccessIoExceptionResponse extends HttpResponse {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: capitalize the C

protected checkAccessIoExceptionResponse(HttpRequest request) {
super(request)
}

@Override
int getStatusCode() {
return 403
}

@Override
String getHeaderValue(String s) {
return request.getHeaders().getValue(s)
}

@Override
HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.put("x-ms-error-code", BlobErrorCode.AUTHORIZATION_FAILURE.toString())
return headers
}

@Override
Flux<ByteBuffer> getBody() {
return Flux.just(ByteBuffer.allocate(0))
}

@Override
Mono<byte[]> getBodyAsByteArray() {
return Mono.just(new byte[0])
}

@Override
Mono<String> getBodyAsString() {
return Mono.just("")
}

@Override
Mono<String> getBodyAsString(Charset charset) {
return Mono.just("")
}
}

def "CheckAccess no file"() {
setup:
def fs = createFS(config)
def path = fs.getPath(generateBlobName())

when:
fs.provider().checkAccess(path)

then:
thrown(NoSuchFileException)
}

def "CheckAccess fs closed"() {
setup:
def fs = createFS(config)
def path = fs.getPath(generateBlobName())
def os = fs.provider().newOutputStream(path)
os.close()

when:
fs.close()
fs.provider().checkAccess(path)

then:
thrown(IOException)
}

@Unroll
def "GetAttributeView"() {
setup:
Expand Down
Loading