This is an automated email from the ASF dual-hosted git repository.

gaul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jclouds.git


The following commit(s) were added to refs/heads/master by this push:
     new 0b68e8a  JCLOUDS-1558: Implement Azure Blob Azure AD auth
0b68e8a is described below

commit 0b68e8adee5e370184311c3fe4931ed8416d12d4
Author: Timur Alperovich <ti...@timuralp.com>
AuthorDate: Mon Aug 9 23:46:24 2021 -0700

    JCLOUDS-1558: Implement Azure Blob Azure AD auth
    
    Implements the Azure AD authentication for Azure Blob, using the OAuth
    module. Added more parameters to the AzureBlob provider:
    - azureblob.auth
    - azureblob.account
    - azureblob.tenantId
    
    The "auth" parameter is used to specify whether Key/SAS auth or Active
    Directory is used. When using Active Directory auth, the identity no
    longer maps to the storage account, which has to be specified
    explicitly. The tenant ID also needs to be supplied to construct the
    auth URL to obtain the token correctly.
---
 providers/azureblob/pom.xml                        |  6 ++
 .../org/jclouds/azure/storage/config/AuthType.java | 37 ++++++++++++
 .../config/AzureStorageOAuthConfigFactory.java     | 59 +++++++++++++++++++
 .../storage/config/AzureStorageProperties.java     | 27 +++++++++
 .../filters/SharedKeyLiteAuthentication.java       | 19 ++++--
 .../util/storageurl/StorageAccountInVhost.java     | 19 +++++-
 .../jclouds/azureblob/AzureBlobApiMetadata.java    | 13 ++++-
 .../azureblob/AzureBlobProviderMetadata.java       | 15 ++++-
 .../binders/BindAzureBlobMetadataToRequest.java    |  2 +-
 .../blobstore/AzureBlobRequestSigner.java          | 25 +++++++-
 .../config/AzureBlobStoreContextModule.java        |  5 +-
 .../azureblob/config/AzureBlobHttpApiModule.java   |  9 ++-
 .../jclouds/azureblob/config/AzureBlobModule.java  |  9 +++
 .../util/storageurl/StorageAccountInVhostTest.java | 13 ++++-
 .../jclouds/azureblob/AzureBlobClientAdTest.java   | 67 ++++++++++++++++++++++
 .../jclouds/azureblob/AzureBlobClientLiveTest.java | 18 +++---
 16 files changed, 315 insertions(+), 28 deletions(-)

diff --git a/providers/azureblob/pom.xml b/providers/azureblob/pom.xml
index 62cfd6e..d9a6451 100644
--- a/providers/azureblob/pom.xml
+++ b/providers/azureblob/pom.xml
@@ -70,6 +70,12 @@
       <artifactId>auto-service</artifactId>
       <optional>true</optional>
     </dependency>
+    <dependency>
+      <groupId>org.apache.jclouds.api</groupId>
+      <artifactId>oauth</artifactId>
+      <version>${project.version}</version>
+      <scope>compile</scope>
+    </dependency>
   </dependencies>
 
   <profiles>
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AuthType.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AuthType.java
new file mode 100644
index 0000000..986f21d
--- /dev/null
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AuthType.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azure.storage.config;
+
+import static com.google.common.base.CaseFormat.LOWER_CAMEL;
+import static com.google.common.base.CaseFormat.UPPER_UNDERSCORE;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public enum AuthType {
+    /** Includes both the API key and SAS credentials */
+    AZURE_KEY,
+    /** Azure AD credentials */
+    AZURE_AD;
+
+    @Override
+    public String toString() {
+        return UPPER_UNDERSCORE.to(LOWER_CAMEL, name());
+    }
+
+    public static AuthType fromValue(String authType) {
+        return valueOf(LOWER_CAMEL.to(UPPER_UNDERSCORE, checkNotNull(authType, 
"authType")));
+    }
+}
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AzureStorageOAuthConfigFactory.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AzureStorageOAuthConfigFactory.java
new file mode 100644
index 0000000..c64ac72
--- /dev/null
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AzureStorageOAuthConfigFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azure.storage.config;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.oauth.v2.config.OAuthConfigFactory;
+import org.jclouds.oauth.v2.config.OAuthScopes;
+
+import static org.jclouds.azure.storage.config.AzureStorageProperties.ACCOUNT;
+import static org.jclouds.oauth.v2.config.OAuthProperties.AUDIENCE;
+import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
+
+public class AzureStorageOAuthConfigFactory implements OAuthConfigFactory {
+    private final OAuthScopes scopes;
+
+    @Named(AUDIENCE)
+    @Inject(optional = true)
+    private String audience;
+
+    @Named(RESOURCE)
+    @Inject(optional = true)
+    private String resource;
+
+    @Named(ACCOUNT)
+    @Inject
+    private String account;
+
+    @Inject
+    AzureStorageOAuthConfigFactory(OAuthScopes scopes) { this.scopes = scopes; 
}
+
+    @Override
+    public OAuthConfig forRequest(HttpRequest input) {
+        String authResource = resource;
+        if (authResource == null) {
+            authResource = "https://"; + account + ".blob.core.windows.net";
+        }
+        String authAudience = audience;
+        if (authAudience == null) {
+            authAudience = "https://"; + account + ".blob.core.windows.net";
+        }
+        return OAuthConfig.create(scopes.forRequest(input), authAudience, 
authResource);
+    }
+}
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AzureStorageProperties.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AzureStorageProperties.java
new file mode 100644
index 0000000..4404e45
--- /dev/null
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/config/AzureStorageProperties.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azure.storage.config;
+
+public final class AzureStorageProperties {
+    public static final String AUTH_TYPE = "jclouds.azureblob.auth";
+    public static final String ACCOUNT = "jclouds.azureblob.account";
+    public static final String TENANT_ID = "jclouds.azureblob.tenantId";
+
+    private AzureStorageProperties() {
+        throw new AssertionError("intentionally unimplemented");
+    }
+}
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
index c2cdf98..2d8f3b6 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/filters/SharedKeyLiteAuthentication.java
@@ -35,6 +35,7 @@ import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import org.jclouds.Constants;
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azure.storage.util.storageurl.StorageUrlSupplier;
 import org.jclouds.crypto.Crypto;
 import org.jclouds.date.TimeStamp;
@@ -47,6 +48,7 @@ import org.jclouds.http.Uris;
 import org.jclouds.http.Uris.UriBuilder;
 import org.jclouds.http.internal.SignatureWire;
 import org.jclouds.logging.Logger;
+import org.jclouds.oauth.v2.filters.OAuthFilter;
 import org.jclouds.util.Strings2;
 
 import com.google.common.annotations.VisibleForTesting;
@@ -80,6 +82,8 @@ public class SharedKeyLiteAuthentication implements 
HttpRequestFilter {
    private final HttpUtils utils;
    private final URI storageUrl;
    private final boolean isSAS;
+   private final AuthType authType;
+   private final OAuthFilter oAuthFilter;
 
    @Resource
    @Named(Constants.LOGGER_SIGNATURE)
@@ -88,8 +92,9 @@ public class SharedKeyLiteAuthentication implements 
HttpRequestFilter {
    @Inject
    public SharedKeyLiteAuthentication(SignatureWire signatureWire,
          @org.jclouds.location.Provider Supplier<Credentials> creds, 
@TimeStamp Provider<String> timeStampProvider,
-         Crypto crypto, HttpUtils utils, @Named("sasAuth") boolean 
sasAuthentication, 
-         StorageUrlSupplier storageUrlSupplier) {
+         Crypto crypto, HttpUtils utils, @Named("sasAuth") boolean 
sasAuthentication,
+         StorageUrlSupplier storageUrlSupplier, AuthType authType,
+         OAuthFilter oAuthFilter) {
       this.crypto = crypto;
       this.utils = utils;
       this.signatureWire = signatureWire;
@@ -98,6 +103,8 @@ public class SharedKeyLiteAuthentication implements 
HttpRequestFilter {
       this.credential = creds.get().credential;
       this.timeStampProvider = timeStampProvider;
       this.isSAS = sasAuthentication;
+      this.authType = authType;
+      this.oAuthFilter = oAuthFilter;
    }
    
    /** 
@@ -105,11 +112,15 @@ public class SharedKeyLiteAuthentication implements 
HttpRequestFilter {
     * is used and applies the right filtering.  
     */
    public HttpRequest filter(HttpRequest request) throws HttpException {
-      request = this.isSAS ? filterSAS(request, this.credential) : 
filterKey(request);
+      if (this.authType == AuthType.AZURE_AD) {
+         request = this.oAuthFilter.filter(request);
+      } else {
+         request = this.isSAS ? filterSAS(request, this.credential) : 
filterKey(request);
+      }
       utils.logRequest(signatureLog, request, "<<");
       return request;
    }
-   
+
    /** 
     * this filter method is applied only for the cases with SAS 
Authentication. 
     */
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhost.java
 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhost.java
index 6835650..0fafac6 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhost.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhost.java
@@ -17,12 +17,15 @@
 package org.jclouds.azure.storage.util.storageurl;
 
 import com.google.common.base.Supplier;
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.domain.Credentials;
 import org.jclouds.location.Provider;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Singleton;
 import java.net.URI;
+import static org.jclouds.azure.storage.config.AzureStorageProperties.ACCOUNT;
 import static 
org.jclouds.azure.storage.util.storageurl.TrailingSlashUtil.ensureTrailingSlash;
 
 @Singleton
@@ -30,11 +33,18 @@ public class StorageAccountInVhost implements 
StorageUrlSupplier {
 
    private final Supplier<URI> endpointSupplier;
    private final Supplier<Credentials> credentialsSupplier;
+   private final AuthType authType;
+   private final String account;
 
    @Inject
-   public StorageAccountInVhost(@Provider Supplier<URI> endpointSupplier, 
@Provider Supplier<Credentials> credentialsSupplier) {
+   public StorageAccountInVhost(@Provider Supplier<URI> endpointSupplier,
+                                @Provider Supplier<Credentials> 
credentialsSupplier,
+                                AuthType authType,
+                                @Named(ACCOUNT) String account) {
       this.endpointSupplier = endpointSupplier;
       this.credentialsSupplier = credentialsSupplier;
+      this.authType = authType;
+      this.account = account;
    }
 
    @Override
@@ -49,7 +59,10 @@ public class StorageAccountInVhost implements 
StorageUrlSupplier {
    }
 
    private String buildUri() {
-      return "https://"; + credentialsSupplier.get().identity + 
".blob.core.windows.net/";
+      String account = this.account;
+      if (account == null) {
+         account = credentialsSupplier.get().identity;
+      }
+      return "https://"; + account + ".blob.core.windows.net/";
    }
-
 }
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java
index 4b5f3eb..16b1d98 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobApiMetadata.java
@@ -16,15 +16,20 @@
  */
 package org.jclouds.azureblob;
 
+import static org.jclouds.azure.storage.config.AzureStorageProperties.ACCOUNT;
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.AUTH_TYPE;
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.TENANT_ID;
 import static 
org.jclouds.blobstore.reference.BlobStoreConstants.PROPERTY_USER_METADATA_PREFIX;
 import static org.jclouds.reflect.Reflection2.typeToken;
 
 import java.net.URI;
 import java.util.Properties;
 
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azureblob.blobstore.config.AzureBlobStoreContextModule;
 import org.jclouds.azureblob.config.AzureBlobHttpApiModule;
 import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.oauth.v2.config.OAuthModule;
 import org.jclouds.rest.internal.BaseHttpApiMetadata;
 
 import com.google.common.collect.ImmutableSet;
@@ -52,6 +57,9 @@ public class AzureBlobApiMetadata extends BaseHttpApiMetadata 
{
    public static Properties defaultProperties() {
       Properties properties = BaseHttpApiMetadata.defaultProperties();
       properties.setProperty(PROPERTY_USER_METADATA_PREFIX, "x-ms-meta-");
+      properties.setProperty(AUTH_TYPE, AuthType.AZURE_KEY.toString());
+      properties.setProperty(ACCOUNT, "");
+      properties.setProperty(TENANT_ID, "");
       return properties;
    }
    
@@ -67,7 +75,10 @@ public class AzureBlobApiMetadata extends 
BaseHttpApiMetadata {
          
.documentation(URI.create("http://msdn.microsoft.com/en-us/library/dd135733.aspx";))
          .defaultProperties(AzureBlobApiMetadata.defaultProperties())
          .view(typeToken(BlobStoreContext.class))
-         .defaultModules(ImmutableSet.<Class<? extends 
Module>>of(AzureBlobHttpApiModule.class, AzureBlobStoreContextModule.class));
+         .defaultModules(ImmutableSet.<Class<? extends Module>>of(
+                 AzureBlobHttpApiModule.class,
+                 AzureBlobStoreContextModule.class,
+                 OAuthModule.class));
       }
       
       @Override
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
index 348fc94..c84a2fe 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/AzureBlobProviderMetadata.java
@@ -24,6 +24,12 @@ import org.jclouds.providers.internal.BaseProviderMetadata;
 
 import com.google.auto.service.AutoService;
 
+import static org.jclouds.azure.storage.config.AzureStorageProperties.ACCOUNT;
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.TENANT_ID;
+import static 
org.jclouds.oauth.v2.config.CredentialType.CLIENT_CREDENTIALS_SECRET;
+import static org.jclouds.oauth.v2.config.OAuthProperties.CREDENTIAL_TYPE;
+import static org.jclouds.oauth.v2.config.OAuthProperties.RESOURCE;
+
 /**
  * Implementation of {@link org.jclouds.types.ProviderMetadata} for Microsoft 
Azure Blob Service.
  */
@@ -38,7 +44,7 @@ public class AzureBlobProviderMetadata extends 
BaseProviderMetadata {
    public Builder toBuilder() {
       return builder().fromProviderMetadata(this);
    }
-   
+
    public AzureBlobProviderMetadata() {
       super(builder());
    }
@@ -49,15 +55,18 @@ public class AzureBlobProviderMetadata extends 
BaseProviderMetadata {
 
    public static Properties defaultProperties() {
       Properties properties = new Properties();
+      properties.put("oauth.endpoint", "https://login.microsoft.com/${"; + 
TENANT_ID + "}/oauth2/token");
+      properties.put(RESOURCE, "https://storage.azure.com";);
+      properties.put(CREDENTIAL_TYPE, CLIENT_CREDENTIALS_SECRET.toString());
+      properties.put(ACCOUNT, "${jclouds.identity}");
       return properties;
    }
    public static class Builder extends BaseProviderMetadata.Builder {
-
       protected Builder() {
             id("azureblob")
             .name("Microsoft Azure Blob Service")
             .apiMetadata(new AzureBlobApiMetadata())
-            .endpoint("https://${jclouds.identity}.blob.core.windows.net";)
+            .endpoint("https://${"; + ACCOUNT + "}.blob.core.windows.net")
             
.homepage(URI.create("http://www.microsoft.com/windowsazure/storage/";))
             .console(URI.create("https://windows.azure.com/default.aspx";))
             .linkedServices("azureblob", "azurequeue", "azuretable")
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureBlobMetadataToRequest.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureBlobMetadataToRequest.java
index 0a384d2..cc1a4ab 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureBlobMetadataToRequest.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/binders/BindAzureBlobMetadataToRequest.java
@@ -77,7 +77,7 @@ public class BindAzureBlobMetadataToRequest implements Binder 
{
          break;
       case BLOCK_BLOB:
          // see 
https://docs.microsoft.com/en-us/rest/api/storageservices/understanding-block-blobs--append-blobs--and-page-blobs
-         // see AzureBlobApiMetadata#version (current API version used is 
2017-04-17)
+         // see AzureBlobApiMetadata#version (current API version used is 
2017-11-09)
          checkArgument(
                
checkNotNull(blob.getPayload().getContentMetadata().getContentLength(), 
"blob.getContentLength()") <= 256L * 1024 * 1024,
                "maximum size for put Blob is 256MB");
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
index 9923898..f058576 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/AzureBlobRequestSigner.java
@@ -26,6 +26,7 @@ import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
 import org.jclouds.azure.storage.util.storageurl.StorageUrlSupplier;
 import org.jclouds.blobstore.BlobRequestSigner;
@@ -57,13 +58,14 @@ public class AzureBlobRequestSigner implements 
BlobRequestSigner {
    private final SharedKeyLiteAuthentication auth;
    private final String credential;
    private final boolean isSAS;
+   private final AuthType authType;
 
    @Inject
    public AzureBlobRequestSigner(
          BlobToHttpGetOptions blob2HttpGetOptions, @TimeStamp Provider<String> 
timeStampProvider,
          DateService dateService, SharedKeyLiteAuthentication auth,
          @org.jclouds.location.Provider Supplier<Credentials> creds, 
@Named("sasAuth") boolean sasAuthentication,
-         StorageUrlSupplier storageUriSupplier)
+         StorageUrlSupplier storageUriSupplier, AuthType authType)
          throws SecurityException, NoSuchMethodException {
       this.identity = creds.get().identity;
       this.credential = creds.get().credential;
@@ -73,6 +75,7 @@ public class AzureBlobRequestSigner implements 
BlobRequestSigner {
       this.dateService = checkNotNull(dateService, "dateService");
       this.auth = auth;
       this.isSAS = sasAuthentication;
+      this.authType = authType;
    }
 
    @Override
@@ -192,12 +195,30 @@ public class AzureBlobRequestSigner implements 
BlobRequestSigner {
             .replaceHeader(HttpHeaders.DATE, nowString);
       request = setHeaders(request, method, options, contentLength, 
contentType);
       return request.build();
-   }  
+   }
+
+   private HttpRequest signAD(String method, String container, String name,
+                              @Nullable GetOptions options, long expires,
+                              @Nullable Long contentLength, @Nullable String 
contentType) {
+      checkNotNull(method, "method");
+      checkNotNull(container, "container");
+      checkNotNull(name, "name");
+      String nowString = timeStampProvider.get();
+      HttpRequest.Builder request = HttpRequest.builder()
+              .method(method)
+              
.endpoint(Uris.uriBuilder(storageUrl).appendPath(container).appendPath(name).build())
+              .replaceHeader(HttpHeaders.DATE, nowString);
+      request = setHeaders(request, method, options, contentLength, 
contentType);
+      return request.build();
+   }
    
    /**
     * modified sign() method, which acts depending on the Auth input. 
     */
    public HttpRequest sign(String method, String container, String name, 
@Nullable GetOptions options, long expires, @Nullable Long contentLength, 
@Nullable String contentType) {
+      if (authType == AuthType.AZURE_AD) {
+         return signAD(method, container, name, options, expires, 
contentLength, contentType);
+      }
       if (isSAS) {
          return signSAS(method, container, name, options, expires, 
contentLength, contentType);
       }
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/config/AzureBlobStoreContextModule.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/config/AzureBlobStoreContextModule.java
index 25e9972..3e48ab5 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/config/AzureBlobStoreContextModule.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/blobstore/config/AzureBlobStoreContextModule.java
@@ -22,6 +22,7 @@ import com.google.common.cache.LoadingCache;
 import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azureblob.AzureBlobClient;
 import org.jclouds.azureblob.blobstore.AzureBlobRequestSigner;
 import org.jclouds.azureblob.blobstore.AzureBlobStore;
@@ -46,12 +47,12 @@ public class AzureBlobStoreContextModule extends 
AbstractModule {
 
    @Provides
    @Singleton
-   protected final LoadingCache<String, PublicAccess> containerAcls(final 
AzureBlobClient client, @Named("sasAuth") final boolean sasAuthentication) {
+   protected final LoadingCache<String, PublicAccess> containerAcls(final 
AzureBlobClient client, @Named("sasAuth") final boolean sasAuthentication, 
AuthType authType) {
       return CacheBuilder.newBuilder().expireAfterWrite(30, 
TimeUnit.SECONDS).build(
                new CacheLoader<String, PublicAccess>() {
                   @Override
                   public PublicAccess load(String container) throws 
CacheLoader.InvalidCacheLoadException {
-                     if (!sasAuthentication) {
+                     if (!sasAuthentication && authType == AuthType.AZURE_KEY) 
{
                         return client.getPublicAccessForContainer(container);
                      }
                      throw new InsufficientAccessRightsException("SAS 
Authentication does not support getAcl and setAcl calls.");
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobHttpApiModule.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobHttpApiModule.java
index 264ad44..82c7b29 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobHttpApiModule.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobHttpApiModule.java
@@ -30,6 +30,8 @@ import java.util.List;
 
 import javax.inject.Named;
 
+import com.google.inject.Scopes;
+import org.jclouds.azure.storage.config.AzureStorageOAuthConfigFactory;
 import org.jclouds.azure.storage.handlers.AzureStorageClientErrorRetryHandler;
 import org.jclouds.azureblob.AzureBlobClient;
 import org.jclouds.azureblob.handlers.ParseAzureBlobErrorFromXmlContent;
@@ -42,6 +44,8 @@ import org.jclouds.http.annotation.Redirection;
 import org.jclouds.http.annotation.ServerError;
 import org.jclouds.json.config.GsonModule.DateAdapter;
 import org.jclouds.json.config.GsonModule.Iso8601DateAdapter;
+import org.jclouds.oauth.v2.config.OAuthConfigFactory;
+import org.jclouds.oauth.v2.config.OAuthScopes;
 import org.jclouds.rest.ConfiguresHttpApi;
 import org.jclouds.rest.config.HttpApiModule;
 import org.jclouds.domain.Credentials;
@@ -55,11 +59,12 @@ import com.google.inject.Provides;
  */
 @ConfiguresHttpApi
 public class AzureBlobHttpApiModule extends HttpApiModule<AzureBlobClient> {
-
    @Override
    protected void configure() {
       install(new AzureBlobModule());
       bind(DateAdapter.class).to(Iso8601DateAdapter.class);
+      bind(OAuthScopes.class).toInstance(OAuthScopes.NoScopes.create());
+      
bind(OAuthConfigFactory.class).to(AzureStorageOAuthConfigFactory.class).in(Scopes.SINGLETON);
       super.configure();
    }
 
@@ -77,7 +82,7 @@ public class AzureBlobHttpApiModule extends 
HttpApiModule<AzureBlobClient> {
     * checks which Authentication type is used 
     */
    @Named("sasAuth")
-   @Provides 
+   @Provides
    protected boolean authSAS(@org.jclouds.location.Provider 
Supplier<Credentials> creds) {
       String credential = creds.get().credential;
       String formattedCredential = credential.startsWith("?") ? 
credential.substring(1) : credential;
diff --git 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobModule.java
 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobModule.java
index ffd4832..f0460ba 100644
--- 
a/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobModule.java
+++ 
b/providers/azureblob/src/main/java/org/jclouds/azureblob/config/AzureBlobModule.java
@@ -17,8 +17,10 @@
 package org.jclouds.azureblob.config;
 
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Provider;
 
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azureblob.domain.AzureBlob;
 import org.jclouds.azureblob.domain.MutableBlobProperties;
 import org.jclouds.azureblob.domain.internal.AzureBlobImpl;
@@ -28,6 +30,8 @@ import com.google.inject.AbstractModule;
 import com.google.inject.Provides;
 import com.google.inject.Scopes;
 
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.AUTH_TYPE;
+
 /**
  * Configures the domain object mappings needed for all Azure Blob 
implementations
  */
@@ -58,4 +62,9 @@ public class AzureBlobModule extends AbstractModule {
       return factory.create(null);
    }
 
+   @Inject
+   @Provides
+   final AuthType AuthTypeFromPropertyOrDefault(@Named(AUTH_TYPE) String 
authType) {
+      return AuthType.fromValue(authType);
+   }
 }
diff --git 
a/providers/azureblob/src/test/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhostTest.java
 
b/providers/azureblob/src/test/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhostTest.java
index 5bf23e8..7e1f4a1 100644
--- 
a/providers/azureblob/src/test/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhostTest.java
+++ 
b/providers/azureblob/src/test/java/org/jclouds/azure/storage/util/storageurl/StorageAccountInVhostTest.java
@@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableSet;
 import com.google.inject.Module;
 import com.google.inject.util.Modules;
 import org.jclouds.ContextBuilder;
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azureblob.config.AppendAccountToEndpointModule;
 import org.jclouds.domain.Credentials;
 import org.jclouds.logging.config.NullLoggingModule;
@@ -40,7 +41,9 @@ public class StorageAccountInVhostTest {
 
       StorageAccountInVhost target = new StorageAccountInVhost(
             () -> null,
-            () -> new Credentials(ACCOUNT, "creds")
+            () -> new Credentials(ACCOUNT, "creds"),
+            AuthType.AZURE_KEY,
+            null
       );
       
       assertEquals(target.get().toString(), 
"https://foo.blob.core.windows.net/";);
@@ -51,7 +54,9 @@ public class StorageAccountInVhostTest {
 
       StorageAccountInVhost target = new StorageAccountInVhost(
             () -> URI.create("https://foo2.blob.core.windows.net/";),
-            () -> new Credentials(ACCOUNT, "creds")
+            () -> new Credentials(ACCOUNT, "creds"),
+            AuthType.AZURE_KEY,
+            ""
       );
 
       assertEquals(target.get().toString(), 
"https://foo2.blob.core.windows.net/";);
@@ -62,7 +67,9 @@ public class StorageAccountInVhostTest {
 
       StorageAccountInVhost target = new StorageAccountInVhost(
             () -> URI.create("https://foo2.blob.core.windows.net/";),
-            () -> new Credentials(ACCOUNT, "creds")
+            () -> new Credentials(ACCOUNT, "creds"),
+            AuthType.AZURE_KEY,
+            ""
       );
 
       assertEquals(target.get().toString(), 
"https://foo2.blob.core.windows.net/";);
diff --git 
a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientAdTest.java
 
b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientAdTest.java
new file mode 100644
index 0000000..8462350
--- /dev/null
+++ 
b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientAdTest.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.azureblob;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.reflect.Invokable;
+import org.jclouds.azure.storage.filters.SharedKeyLiteAuthentication;
+import org.jclouds.azure.storage.options.ListOptions;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.internal.BaseRestAnnotationProcessingTest;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import static org.jclouds.azure.storage.config.AzureStorageProperties.ACCOUNT;
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.AUTH_TYPE;
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.TENANT_ID;
+import static org.jclouds.reflect.Reflection2.method;
+import static org.testng.Assert.assertEquals;
+
+@Test(groups = "unit", testName = "AzureBlobClientAdTest")
+public class AzureBlobClientAdTest extends 
BaseRestAnnotationProcessingTest<AzureBlobClient> {
+    @Override
+    protected void checkFilters(HttpRequest request) {
+        assertEquals(request.getFilters().size(), 1);
+        assertEquals(request.getFilters().get(0).getClass(), 
SharedKeyLiteAuthentication.class);
+    }
+
+    @Override
+    public AzureBlobProviderMetadata createProviderMetadata() {
+        return new AzureBlobProviderMetadata();
+    }
+
+    @Override
+    protected Properties setupProperties() {
+        Properties adProperties = new Properties();
+        adProperties.setProperty(TENANT_ID, "tenant");
+        adProperties.setProperty(ACCOUNT, "ad-account");
+        adProperties.setProperty(AUTH_TYPE, "azureAd");
+        return adProperties;
+    }
+
+    public void testListContainersAD() throws SecurityException, 
NoSuchMethodException, IOException {
+        Invokable<?, ?> method = method(AzureBlobClient.class, 
"listContainers", ListOptions[].class);
+        GeneratedHttpRequest request = processor.createRequest(method, 
ImmutableList.of());
+
+        assertRequestLineEquals(request, "GET 
https://ad-account.blob.core.windows.net/?comp=list HTTP/1.1");
+        assertNonPayloadHeadersEqual(request, "x-ms-version: 2017-11-09\n");
+        assertPayloadEquals(request, null, null, false);
+    }
+}
diff --git 
a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
 
b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
index 7d727df..6734adf 100644
--- 
a/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
+++ 
b/providers/azureblob/src/test/java/org/jclouds/azureblob/AzureBlobClientLiveTest.java
@@ -19,6 +19,7 @@ package org.jclouds.azureblob;
 import static com.google.common.io.BaseEncoding.base16;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Fail.failBecauseExceptionWasNotThrown;
+import static 
org.jclouds.azure.storage.config.AzureStorageProperties.AUTH_TYPE;
 import static 
org.jclouds.azure.storage.options.ListOptions.Builder.includeMetadata;
 import static 
org.jclouds.azureblob.options.CreateContainerOptions.Builder.withMetadata;
 import static 
org.jclouds.azureblob.options.CreateContainerOptions.Builder.withPublicAccess;
@@ -36,6 +37,7 @@ import java.util.Map;
 import java.util.Set;
 
 import org.jclouds.azure.storage.AzureStorageResponseException;
+import org.jclouds.azure.storage.config.AuthType;
 import org.jclouds.azure.storage.domain.BoundedSet;
 import org.jclouds.azure.storage.options.ListOptions;
 import org.jclouds.azureblob.domain.AccessTier;
@@ -59,6 +61,7 @@ import org.jclouds.io.Payloads;
 import org.jclouds.util.Strings2;
 import org.jclouds.util.Throwables2;
 import org.jclouds.utils.TestUtils;
+import org.testng.SkipException;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Charsets;
@@ -71,9 +74,7 @@ import com.google.common.io.ByteSource;
 
 @Test(groups = "live", singleThreaded = true)
 public class AzureBlobClientLiveTest extends BaseBlobStoreIntegrationTest {
-   public AzureBlobClientLiveTest() {
-      provider = "azureblob";
-   }
+   public AzureBlobClientLiveTest() { provider = "azureblob"; }
 
    public AzureBlobClient getApi() {
       return view.unwrapApi(AzureBlobClient.class);
@@ -111,8 +112,8 @@ public class AzureBlobClientLiveTest extends 
BaseBlobStoreIntegrationTest {
       long containerCount = response.size();
       assertTrue(containerCount >= 1);
       ListBlobsResponse list = getApi().listBlobs(privateContainer);
-      assertEquals(list.getUrl(), 
URI.create(String.format("https://%s.blob.core.windows.net/%s";,
-            view.unwrap().getIdentity(), privateContainer)));
+      assertThat(list.getUrl().toString()).endsWith(
+              String.format(".blob.core.windows.net/%s", privateContainer));
       // TODO .. check to see the container actually exists
    }
 
@@ -174,8 +175,7 @@ public class AzureBlobClientLiveTest extends 
BaseBlobStoreIntegrationTest {
          }
       }
       ListBlobsResponse list = getApi().listBlobs();
-      assertEquals(list.getUrl(), 
URI.create(String.format("https://%s.blob.core.windows.net/$root";,
-            view.unwrap().getIdentity())));
+      
assertThat(list.getUrl().toString()).endsWith(".blob.core.windows.net/$root");
    }
 
    @Test
@@ -388,6 +388,10 @@ public class AzureBlobClientLiveTest extends 
BaseBlobStoreIntegrationTest {
 
    @Test
    public void testGetSetACL() throws Exception {
+      String authType = System.getProperty(AUTH_TYPE);
+      if (authType != null && AuthType.fromValue(authType) == 
AuthType.AZURE_AD) {
+         throw new SkipException("Get/Set ACL unsupported with Azure AD");
+      }
       AzureBlobClient client = getApi();
       String blockContainer = CONTAINER_PREFIX + 
containerIndex.incrementAndGet();
       client.createContainer(blockContainer);

Reply via email to