mirror of https://github.com/apache/jclouds.git
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.
This commit is contained in:
parent
519bee9f60
commit
0b68e8adee
|
@ -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>
|
||||
|
|
|
@ -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")));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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/";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue