From 8b7ea16fdf902cbfeccda88153e292ed629448a7 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Thu, 6 May 2021 17:05:59 +0800 Subject: [PATCH 01/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105061703 by LiHong --- .../identity/EventHubSharedKeyCredential.java | 142 ++++++++++++++++++ .../EventHubSharedKeyCredentialBuilder.java | 69 +++++++++ .../EventHubSharedKeyCredentialTest.java | 73 +++++++++ 3 files changed, 284 insertions(+) create mode 100644 sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java create mode 100644 sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java create mode 100644 sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java new file mode 100644 index 000000000000..6f100d1c184c --- /dev/null +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java @@ -0,0 +1,142 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +import com.azure.core.annotation.Immutable; +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.logging.ClientLogger; +import reactor.core.publisher.Mono; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.Base64; +import java.util.Locale; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Authorizes with Azure Event Hub service using a shared access key from either an Event Hub namespace or a specific + * Event Hub. + * + *

+ * The shared access key can be obtained by creating a shared access policy for the Event Hub namespace or for + * a specific Event Hub instance. See + * Shared access authorization policies + * for more information. + *

+ * + * @see Authorize + * access with shared access signature. + */ +@Immutable +public class EventHubSharedKeyCredential implements TokenCredential { + private static final String SHARED_ACCESS_SIGNATURE_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s"; + private static final String HASH_ALGORITHM = "HMACSHA256"; + public static final Duration TOKEN_VALIDITY = Duration.ofMinutes(20); + + private final String sharedAccessPolicy; + private final SecretKeySpec secretKeySpec; + private final String sharedAccessSignature; + private final ClientLogger logger = new ClientLogger(EventHubSharedKeyCredential.class); + + /** + * Creates an instance that authorizes using the {@code sharedAccessPolicy} and {@code sharedAccessKey} + * and {@code sharedAccessSignature}. + * + * @param sharedAccessPolicy Name of the shared access key policy. + * @param secretKeySpec Value of the shared access key. + * @param sharedAccessSignature Value of the shared access signature. + */ + public EventHubSharedKeyCredential(String sharedAccessPolicy, SecretKeySpec secretKeySpec, + String sharedAccessSignature) { + this.sharedAccessPolicy = sharedAccessPolicy; + this.secretKeySpec = secretKeySpec; + this.sharedAccessSignature = sharedAccessSignature; + } + + /** + * Retrieves the token, given the audience/resources requested, for use in authorization against an Event Hub + * namespace or a specific Event Hub instance. + * + * @param request The details of a token request + * @return A Mono that completes and returns the shared access signature. + * @throws IllegalArgumentException if {@code scopes} does not contain a single value, which is the token + * audience. + */ + @Override + public Mono getToken(TokenRequestContext request) { + return Mono.fromCallable(() -> generateSharedAccessSignature(request.getScopes().get(0))); + } + + private AccessToken generateSharedAccessSignature(final String resource) throws UnsupportedEncodingException { + if (CoreUtils.isNullOrEmpty(resource)) { + throw logger.logExceptionAsError(new IllegalArgumentException("resource cannot be empty")); + } + + if (sharedAccessSignature != null) { + return new AccessToken(sharedAccessSignature, getExpirationTime(sharedAccessSignature)); + } + + final Mac hmac; + try { + hmac = Mac.getInstance(HASH_ALGORITHM); + hmac.init(secretKeySpec); + } catch (NoSuchAlgorithmException e) { + throw logger.logExceptionAsError(new UnsupportedOperationException( + String.format("Unable to create hashing algorithm '%s'", HASH_ALGORITHM), e)); + } catch (InvalidKeyException e) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "'sharedAccessKey' is an invalid value for the hashing algorithm.", e)); + } + + final String utf8Encoding = UTF_8.name(); + final OffsetDateTime expiresOn = OffsetDateTime.now(ZoneOffset.UTC).plus(TOKEN_VALIDITY); + final String expiresOnEpochSeconds = Long.toString(expiresOn.toEpochSecond()); + final String audienceUri = URLEncoder.encode(resource, utf8Encoding); + final String secretToSign = audienceUri + "\n" + expiresOnEpochSeconds; + + final byte[] signatureBytes = hmac.doFinal(secretToSign.getBytes(utf8Encoding)); + final String signature = Base64.getEncoder().encodeToString(signatureBytes); + + final String token = String.format(Locale.US, SHARED_ACCESS_SIGNATURE_FORMAT, + audienceUri, + URLEncoder.encode(signature, utf8Encoding), + URLEncoder.encode(expiresOnEpochSeconds, utf8Encoding), + URLEncoder.encode(sharedAccessPolicy, utf8Encoding)); + return new AccessToken(token, expiresOn); + } + + private OffsetDateTime getExpirationTime(String sharedAccessSignature) { + String[] parts = sharedAccessSignature.split("&"); + return Arrays.stream(parts) + .map(part -> part.split("=")) + .filter(pair -> pair.length == 2 && pair[0].equalsIgnoreCase("se")) + .findFirst() + .map(pair -> pair[1]) + .map(expirationTimeStr -> { + try { + long epochSeconds = Long.parseLong(expirationTimeStr); + return Instant.ofEpochSecond(epochSeconds).atOffset(ZoneOffset.UTC); + } catch (NumberFormatException exception) { + logger.verbose("Invalid expiration time format in the SAS token: {}. Falling back to max " + + "expiration time.", expirationTimeStr); + return OffsetDateTime.MAX; + } + }) + .orElse(OffsetDateTime.MAX); + } +} diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java new file mode 100644 index 000000000000..14fb3419b1fe --- /dev/null +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java @@ -0,0 +1,69 @@ +package com.azure.identity; + +import com.azure.identity.implementation.util.ValidationUtil; + +import javax.crypto.spec.SecretKeySpec; +import java.util.HashMap; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class EventHubSharedKeyCredentialBuilder extends AadCredentialBuilderBase { + + private static final String HASH_ALGORITHM = "HMACSHA256"; + + private String sharedAccessPolicy; + private SecretKeySpec secretKeySpec; + private String sharedAccessSignature; + + /** + * Sets the sharedAccessPolicy of the user. + * + * @param sharedAccessPolicy the sharedAccessPolicy of the user + * @return the EventHubSharedKeyCredentialBuilder itself + */ + public EventHubSharedKeyCredentialBuilder sharedAccessPolicy(String sharedAccessPolicy) { + this.sharedAccessPolicy = sharedAccessPolicy; + return this; + } + + /** + * Sets the shardAccessKey of the user. + * + * @param sharedAccessKey the sharedAccessKey of the user + * @return the ServiceBusSharedKeyCredentialBuilder itself + */ + public EventHubSharedKeyCredentialBuilder sharedAccessKey(String sharedAccessKey) { + this.secretKeySpec = new SecretKeySpec(sharedAccessKey.getBytes(UTF_8),HASH_ALGORITHM); + return this; + } + + /** + * Sets the sharedAccessSignature of the user. + * + * @param sharedAccessSignature the sharedAccessSignature of the user + * @return the ServiceBusSharedKeyCredentialBuilder itself + */ + public EventHubSharedKeyCredentialBuilder sharedAccessSignature(String sharedAccessSignature) { + this.sharedAccessSignature = sharedAccessSignature; + return this; + } + + /** + * Creates a new {@link EventHubSharedKeyCredential} with the current configurations. + * + * @return a {@link EventHubSharedKeyCredential} with the current configurations. + */ + public EventHubSharedKeyCredential build() { + if (sharedAccessSignature == null) { + ValidationUtil.validate(getClass().getSimpleName(), new HashMap() {{ + put("sharedAccessPolicy", sharedAccessPolicy); + put("sharedAccessKey", secretKeySpec); + }}); + } else { + ValidationUtil.validate(getClass().getSimpleName(), new HashMap() {{ + put("sharedAccessSignature", sharedAccessSignature); + }}); + } + return new EventHubSharedKeyCredential(sharedAccessPolicy, secretKeySpec, sharedAccessSignature); + } +} diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java new file mode 100644 index 000000000000..9464802e8605 --- /dev/null +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java @@ -0,0 +1,73 @@ +package com.azure.identity; + +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenRequestContext; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(fullyQualifiedNames = "com.azure.identity.*") +@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"}) +public class EventHubSharedKeyCredentialTest { + + private static final String sharedAccessPolicy = ""; + + private static final String sharedAccessKey = ""; + + private static final String sharedAccessSignature = ""; + + private static final TokenRequestContext request = new TokenRequestContext().addScopes(""); + + @Test + public void testValidShardAccessKey() throws Exception { + + // mock + EventHubSharedKeyCredential ServiceBusSharedKeyCredential = PowerMockito.mock(EventHubSharedKeyCredential.class); + when(ServiceBusSharedKeyCredential.getToken(any(TokenRequestContext.class))) + .thenReturn(getMockAccessToken(sharedAccessSignature,OffsetDateTime.now(ZoneOffset.UTC))); + PowerMockito.whenNew(EventHubSharedKeyCredential.class).withAnyArguments().thenReturn(ServiceBusSharedKeyCredential); + + EventHubSharedKeyCredential credential + = new EventHubSharedKeyCredentialBuilder() + .sharedAccessKey(sharedAccessKey) + .sharedAccessPolicy(sharedAccessPolicy) + .build(); + StepVerifier.create(credential.getToken(request)) + .expectNextMatches(accessToken -> sharedAccessSignature.equals(accessToken.getToken())) + .verifyComplete(); + } + + @Test + public void testValidSharedAccessSignature(){ + EventHubSharedKeyCredential credential + = new EventHubSharedKeyCredentialBuilder() + .sharedAccessSignature(sharedAccessSignature) + .build(); + StepVerifier.create(credential.getToken(request)) + .expectNextMatches(accessToken -> sharedAccessSignature.equals(accessToken.getToken())) + .verifyComplete(); + } + + /** + * Creates a mock {@link AccessToken} instance. + * @param accessToken the access token to return + * @param expiresOn the expiration time + * @return a Mono publisher of the result + */ + public static Mono getMockAccessToken(String accessToken, OffsetDateTime expiresOn) { + return Mono.just(new AccessToken(accessToken, expiresOn.plusMinutes(20))); + } + +} From 8be863b668ceaaaf7784695f75790f78308b43a9 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Fri, 7 May 2021 10:18:17 +0800 Subject: [PATCH 02/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105071018 by LiHong --- .../identity/EventHubSharedKeyCredential.java | 139 ++++++++++++++++++ .../EventHubSharedKeyCredentialBuilder.java | 69 +++++++++ .../EventHubSharedKeyCredentialTest.java | 73 +++++++++ 3 files changed, 281 insertions(+) create mode 100644 sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java create mode 100644 sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java create mode 100644 sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java new file mode 100644 index 000000000000..00d69f89edfb --- /dev/null +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java @@ -0,0 +1,139 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.identity; + +import com.azure.core.annotation.Immutable; +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.TokenRequestContext; +import com.azure.core.util.CoreUtils; +import com.azure.core.util.logging.ClientLogger; +import reactor.core.publisher.Mono; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.Duration; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.Arrays; +import java.util.Base64; +import java.util.Locale; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Authorizes with Azure Event Hub service using a shared access key from either an Event Hub namespace or a specific + * Event Hub. + * + *

+ * The shared access key can be obtained by creating a shared access policy for the Event Hub namespace or for + * a specific Event Hub instance. See + * Shared access authorization policies for more information. + *

+ * + * @see Authorizeaccess with shared access signature. + */ +@Immutable +public class EventHubSharedKeyCredential implements TokenCredential { + private static final String SHARED_ACCESS_SIGNATURE_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s"; + private static final String HASH_ALGORITHM = "HMACSHA256"; + public static final Duration TOKEN_VALIDITY = Duration.ofMinutes(20); + + private final String sharedAccessPolicy; + private final SecretKeySpec secretKeySpec; + private final String sharedAccessSignature; + private final ClientLogger logger = new ClientLogger(EventHubSharedKeyCredential.class); + + /** + * Creates an instance that authorizes using the {@code sharedAccessPolicy} and {@code sharedAccessKey} + * and {@code sharedAccessSignature}. + * + * @param sharedAccessPolicy Name of the shared access key policy. + * @param secretKeySpec Value of the shared access key. + * @param sharedAccessSignature Value of the shared access signature. + */ + public EventHubSharedKeyCredential(String sharedAccessPolicy, SecretKeySpec secretKeySpec, + String sharedAccessSignature) { + this.sharedAccessPolicy = sharedAccessPolicy; + this.secretKeySpec = secretKeySpec; + this.sharedAccessSignature = sharedAccessSignature; + } + + /** + * Retrieves the token, given the audience/resources requested, for use in authorization against an Event Hub + * namespace or a specific Event Hub instance. + * + * @param request The details of a token request + * @return A Mono that completes and returns the shared access signature. + * @throws IllegalArgumentException if {@code scopes} does not contain a single value, which is the token + * audience. + */ + @Override + public Mono getToken(TokenRequestContext request) { + return Mono.fromCallable(() -> generateSharedAccessSignature(request.getScopes().get(0))); + } + + private AccessToken generateSharedAccessSignature(final String resource) throws UnsupportedEncodingException { + if (CoreUtils.isNullOrEmpty(resource)) { + throw logger.logExceptionAsError(new IllegalArgumentException("resource cannot be empty")); + } + + if (sharedAccessSignature != null) { + return new AccessToken(sharedAccessSignature, getExpirationTime(sharedAccessSignature)); + } + + final Mac hmac; + try { + hmac = Mac.getInstance(HASH_ALGORITHM); + hmac.init(secretKeySpec); + } catch (NoSuchAlgorithmException e) { + throw logger.logExceptionAsError(new UnsupportedOperationException( + String.format("Unable to create hashing algorithm '%s'", HASH_ALGORITHM), e)); + } catch (InvalidKeyException e) { + throw logger.logExceptionAsError(new IllegalArgumentException( + "'sharedAccessKey' is an invalid value for the hashing algorithm.", e)); + } + + final String utf8Encoding = UTF_8.name(); + final OffsetDateTime expiresOn = OffsetDateTime.now(ZoneOffset.UTC).plus(TOKEN_VALIDITY); + final String expiresOnEpochSeconds = Long.toString(expiresOn.toEpochSecond()); + final String audienceUri = URLEncoder.encode(resource, utf8Encoding); + final String secretToSign = audienceUri + "\n" + expiresOnEpochSeconds; + + final byte[] signatureBytes = hmac.doFinal(secretToSign.getBytes(utf8Encoding)); + final String signature = Base64.getEncoder().encodeToString(signatureBytes); + + final String token = String.format(Locale.US, SHARED_ACCESS_SIGNATURE_FORMAT, + audienceUri, + URLEncoder.encode(signature, utf8Encoding), + URLEncoder.encode(expiresOnEpochSeconds, utf8Encoding), + URLEncoder.encode(sharedAccessPolicy, utf8Encoding)); + return new AccessToken(token, expiresOn); + } + + private OffsetDateTime getExpirationTime(String sharedAccessSignature) { + String[] parts = sharedAccessSignature.split("&"); + return Arrays.stream(parts) + .map(part -> part.split("=")) + .filter(pair -> pair.length == 2 && pair[0].equalsIgnoreCase("se")) + .findFirst() + .map(pair -> pair[1]) + .map(expirationTimeStr -> { + try { + long epochSeconds = Long.parseLong(expirationTimeStr); + return Instant.ofEpochSecond(epochSeconds).atOffset(ZoneOffset.UTC); + } catch (NumberFormatException exception) { + logger.verbose("Invalid expiration time format in the SAS token: {}. Falling back to max " + + "expiration time.", expirationTimeStr); + return OffsetDateTime.MAX; + } + }) + .orElse(OffsetDateTime.MAX); + } +} diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java new file mode 100644 index 000000000000..14fb3419b1fe --- /dev/null +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java @@ -0,0 +1,69 @@ +package com.azure.identity; + +import com.azure.identity.implementation.util.ValidationUtil; + +import javax.crypto.spec.SecretKeySpec; +import java.util.HashMap; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class EventHubSharedKeyCredentialBuilder extends AadCredentialBuilderBase { + + private static final String HASH_ALGORITHM = "HMACSHA256"; + + private String sharedAccessPolicy; + private SecretKeySpec secretKeySpec; + private String sharedAccessSignature; + + /** + * Sets the sharedAccessPolicy of the user. + * + * @param sharedAccessPolicy the sharedAccessPolicy of the user + * @return the EventHubSharedKeyCredentialBuilder itself + */ + public EventHubSharedKeyCredentialBuilder sharedAccessPolicy(String sharedAccessPolicy) { + this.sharedAccessPolicy = sharedAccessPolicy; + return this; + } + + /** + * Sets the shardAccessKey of the user. + * + * @param sharedAccessKey the sharedAccessKey of the user + * @return the ServiceBusSharedKeyCredentialBuilder itself + */ + public EventHubSharedKeyCredentialBuilder sharedAccessKey(String sharedAccessKey) { + this.secretKeySpec = new SecretKeySpec(sharedAccessKey.getBytes(UTF_8),HASH_ALGORITHM); + return this; + } + + /** + * Sets the sharedAccessSignature of the user. + * + * @param sharedAccessSignature the sharedAccessSignature of the user + * @return the ServiceBusSharedKeyCredentialBuilder itself + */ + public EventHubSharedKeyCredentialBuilder sharedAccessSignature(String sharedAccessSignature) { + this.sharedAccessSignature = sharedAccessSignature; + return this; + } + + /** + * Creates a new {@link EventHubSharedKeyCredential} with the current configurations. + * + * @return a {@link EventHubSharedKeyCredential} with the current configurations. + */ + public EventHubSharedKeyCredential build() { + if (sharedAccessSignature == null) { + ValidationUtil.validate(getClass().getSimpleName(), new HashMap() {{ + put("sharedAccessPolicy", sharedAccessPolicy); + put("sharedAccessKey", secretKeySpec); + }}); + } else { + ValidationUtil.validate(getClass().getSimpleName(), new HashMap() {{ + put("sharedAccessSignature", sharedAccessSignature); + }}); + } + return new EventHubSharedKeyCredential(sharedAccessPolicy, secretKeySpec, sharedAccessSignature); + } +} diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java new file mode 100644 index 000000000000..9464802e8605 --- /dev/null +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java @@ -0,0 +1,73 @@ +package com.azure.identity; + +import com.azure.core.credential.AccessToken; +import com.azure.core.credential.TokenRequestContext; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.OffsetDateTime; +import java.time.ZoneOffset; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(fullyQualifiedNames = "com.azure.identity.*") +@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"}) +public class EventHubSharedKeyCredentialTest { + + private static final String sharedAccessPolicy = ""; + + private static final String sharedAccessKey = ""; + + private static final String sharedAccessSignature = ""; + + private static final TokenRequestContext request = new TokenRequestContext().addScopes(""); + + @Test + public void testValidShardAccessKey() throws Exception { + + // mock + EventHubSharedKeyCredential ServiceBusSharedKeyCredential = PowerMockito.mock(EventHubSharedKeyCredential.class); + when(ServiceBusSharedKeyCredential.getToken(any(TokenRequestContext.class))) + .thenReturn(getMockAccessToken(sharedAccessSignature,OffsetDateTime.now(ZoneOffset.UTC))); + PowerMockito.whenNew(EventHubSharedKeyCredential.class).withAnyArguments().thenReturn(ServiceBusSharedKeyCredential); + + EventHubSharedKeyCredential credential + = new EventHubSharedKeyCredentialBuilder() + .sharedAccessKey(sharedAccessKey) + .sharedAccessPolicy(sharedAccessPolicy) + .build(); + StepVerifier.create(credential.getToken(request)) + .expectNextMatches(accessToken -> sharedAccessSignature.equals(accessToken.getToken())) + .verifyComplete(); + } + + @Test + public void testValidSharedAccessSignature(){ + EventHubSharedKeyCredential credential + = new EventHubSharedKeyCredentialBuilder() + .sharedAccessSignature(sharedAccessSignature) + .build(); + StepVerifier.create(credential.getToken(request)) + .expectNextMatches(accessToken -> sharedAccessSignature.equals(accessToken.getToken())) + .verifyComplete(); + } + + /** + * Creates a mock {@link AccessToken} instance. + * @param accessToken the access token to return + * @param expiresOn the expiration time + * @return a Mono publisher of the result + */ + public static Mono getMockAccessToken(String accessToken, OffsetDateTime expiresOn) { + return Mono.just(new AccessToken(accessToken, expiresOn.plusMinutes(20))); + } + +} From f88ce76bca02549e6d3864ad22a8d5f0e35147cb Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Fri, 7 May 2021 10:58:53 +0800 Subject: [PATCH 03/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105071058 by LiHong --- .../identity/EventHubSharedKeyCredential.java | 1 + .../EventHubSharedKeyCredentialBuilder.java | 26 +++++++------- .../implementation/util/ValidationUtil.java | 17 ++++++++++ .../EventHubSharedKeyCredentialTest.java | 34 ++++++++++--------- 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java index 00d69f89edfb..94d3ccc8e4f0 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java @@ -41,6 +41,7 @@ */ @Immutable public class EventHubSharedKeyCredential implements TokenCredential { + private static final String SHARED_ACCESS_SIGNATURE_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s"; private static final String HASH_ALGORITHM = "HMACSHA256"; public static final Duration TOKEN_VALIDITY = Duration.ofMinutes(20); diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java index 14fb3419b1fe..2cea4b06e690 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java @@ -1,12 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. package com.azure.identity; import com.azure.identity.implementation.util.ValidationUtil; - import javax.crypto.spec.SecretKeySpec; import java.util.HashMap; +import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; +/** + * Fluent credential builder for instantiating a {@link EventHubSharedKeyCredential}. + * + * @see EventHubSharedKeyCredential + */ public class EventHubSharedKeyCredentialBuilder extends AadCredentialBuilderBase { private static final String HASH_ALGORITHM = "HMACSHA256"; @@ -33,7 +40,7 @@ public EventHubSharedKeyCredentialBuilder sharedAccessPolicy(String sharedAccess * @return the ServiceBusSharedKeyCredentialBuilder itself */ public EventHubSharedKeyCredentialBuilder sharedAccessKey(String sharedAccessKey) { - this.secretKeySpec = new SecretKeySpec(sharedAccessKey.getBytes(UTF_8),HASH_ALGORITHM); + this.secretKeySpec = new SecretKeySpec(sharedAccessKey.getBytes(UTF_8), HASH_ALGORITHM); return this; } @@ -54,16 +61,11 @@ public EventHubSharedKeyCredentialBuilder sharedAccessSignature(String sharedAcc * @return a {@link EventHubSharedKeyCredential} with the current configurations. */ public EventHubSharedKeyCredential build() { - if (sharedAccessSignature == null) { - ValidationUtil.validate(getClass().getSimpleName(), new HashMap() {{ - put("sharedAccessPolicy", sharedAccessPolicy); - put("sharedAccessKey", secretKeySpec); - }}); - } else { - ValidationUtil.validate(getClass().getSimpleName(), new HashMap() {{ - put("sharedAccessSignature", sharedAccessSignature); - }}); - } + Map validationMap = new HashMap(); + validationMap.put("sharedAccessPolicy", sharedAccessPolicy); + validationMap.put("sharedAccessKey", secretKeySpec); + validationMap.put("sharedAccessSignature", sharedAccessSignature); + ValidationUtil.validateForSharedAccessKey(getClass().getSimpleName(), validationMap); return new EventHubSharedKeyCredential(sharedAccessPolicy, secretKeySpec, sharedAccessSignature); } } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java index 2ace21a1d2a8..3a46986e3aed 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java @@ -18,6 +18,23 @@ public final class ValidationUtil { private static Pattern tenantIdentifierCharPattern = Pattern.compile("^(?:[A-Z]|[0-9]|[a-z]|-|.)+$"); + public static void validateForSharedAccessKey(String className, Map parameters) { + ClientLogger logger = new ClientLogger(className); + List missing = new ArrayList<>(); + if (parameters.get("sharedAccessSignature") == null) { + parameters.remove("sharedAccessSignature"); + for (Map.Entry entry : parameters.entrySet()) { + if (entry.getValue() == null) { + missing.add(entry.getKey()); + } + } + if (missing.size() > 0) { + throw logger.logExceptionAsWarning(new IllegalArgumentException("Must provide non-null values for " + + String.join(", ", missing) + " properties in " + className)); + } + } + } + public static void validate(String className, Map parameters) { ClientLogger logger = new ClientLogger(className); List missing = new ArrayList<>(); diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java index 9464802e8605..b70584a143de 100644 --- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java @@ -1,3 +1,5 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. package com.azure.identity; import com.azure.core.credential.AccessToken; @@ -22,41 +24,41 @@ @PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"}) public class EventHubSharedKeyCredentialTest { - private static final String sharedAccessPolicy = ""; + private static final String SHARED_ACCESS_POLICY = ""; - private static final String sharedAccessKey = ""; + private static final String SHARED_ACCESS_KEY = ""; - private static final String sharedAccessSignature = ""; + private static final String SHARED_ACCESS_SIGNATURE = ""; - private static final TokenRequestContext request = new TokenRequestContext().addScopes(""); + private static final TokenRequestContext REQUEST = new TokenRequestContext().addScopes(""); @Test public void testValidShardAccessKey() throws Exception { // mock - EventHubSharedKeyCredential ServiceBusSharedKeyCredential = PowerMockito.mock(EventHubSharedKeyCredential.class); - when(ServiceBusSharedKeyCredential.getToken(any(TokenRequestContext.class))) - .thenReturn(getMockAccessToken(sharedAccessSignature,OffsetDateTime.now(ZoneOffset.UTC))); - PowerMockito.whenNew(EventHubSharedKeyCredential.class).withAnyArguments().thenReturn(ServiceBusSharedKeyCredential); + EventHubSharedKeyCredential eventHubSharedKeyCredential = PowerMockito.mock(EventHubSharedKeyCredential.class); + when(eventHubSharedKeyCredential.getToken(any(TokenRequestContext.class))) + .thenReturn(getMockAccessToken(SHARED_ACCESS_SIGNATURE, OffsetDateTime.now(ZoneOffset.UTC))); + PowerMockito.whenNew(EventHubSharedKeyCredential.class).withAnyArguments().thenReturn(eventHubSharedKeyCredential); EventHubSharedKeyCredential credential = new EventHubSharedKeyCredentialBuilder() - .sharedAccessKey(sharedAccessKey) - .sharedAccessPolicy(sharedAccessPolicy) + .sharedAccessKey(SHARED_ACCESS_KEY) + .sharedAccessPolicy(SHARED_ACCESS_POLICY) .build(); - StepVerifier.create(credential.getToken(request)) - .expectNextMatches(accessToken -> sharedAccessSignature.equals(accessToken.getToken())) + StepVerifier.create(credential.getToken(REQUEST)) + .expectNextMatches(accessToken -> SHARED_ACCESS_SIGNATURE.equals(accessToken.getToken())) .verifyComplete(); } @Test - public void testValidSharedAccessSignature(){ + public void testValidSharedAccessSignature() { EventHubSharedKeyCredential credential = new EventHubSharedKeyCredentialBuilder() - .sharedAccessSignature(sharedAccessSignature) + .sharedAccessSignature(SHARED_ACCESS_SIGNATURE) .build(); - StepVerifier.create(credential.getToken(request)) - .expectNextMatches(accessToken -> sharedAccessSignature.equals(accessToken.getToken())) + StepVerifier.create(credential.getToken(REQUEST)) + .expectNextMatches(accessToken -> SHARED_ACCESS_SIGNATURE.equals(accessToken.getToken())) .verifyComplete(); } From ba6c74b08d6dfbae2b40e91765a0984b2e7c2e90 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Thu, 13 May 2021 13:42:48 +0800 Subject: [PATCH 04/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105131342 by LiHong --- .../eventhubs/EventHubClientBuilder.java | 79 ++++++++++ .../eventhubs/EventHubClientBuilderTest.java | 23 +++ .../identity/EventHubSharedKeyCredential.java | 140 ------------------ .../EventHubSharedKeyCredentialBuilder.java | 71 --------- .../implementation/util/ValidationUtil.java | 17 --- .../EventHubSharedKeyCredentialTest.java | 75 ---------- 6 files changed, 102 insertions(+), 303 deletions(-) delete mode 100644 sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java delete mode 100644 sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java delete mode 100644 sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java b/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java index ca295d97cec8..3381154015bd 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java @@ -20,6 +20,8 @@ import com.azure.core.annotation.ServiceClientBuilder; import com.azure.core.annotation.ServiceClientProtocol; import com.azure.core.credential.TokenCredential; +import com.azure.core.credential.AzureNamedKeyCredential; +import com.azure.core.credential.AzureSasCredential; import com.azure.core.exception.AzureException; import com.azure.core.util.ClientOptions; import com.azure.core.util.Configuration; @@ -202,6 +204,15 @@ private TokenCredential getTokenCredential(ConnectionStringProperties properties return tokenCredential; } + private TokenCredential getTokenCredential(AzureNamedKeyCredential credential) { + return new EventHubSharedKeyCredential(credential.getAzureNamedKey().getName(), + credential.getAzureNamedKey().getKey(), ClientConstants.TOKEN_VALIDITY); + } + + private TokenCredential getTokenCredential(AzureSasCredential credential) { + return new EventHubSharedKeyCredential(credential.getSignature()); + } + /** * Sets the client options. * @@ -340,6 +351,74 @@ public EventHubClientBuilder credential(String fullyQualifiedNamespace, String e return this; } + /** + * Sets the credential information for which Event Hub instance to connect to, and how to authorize against it. + * + * @param fullyQualifiedNamespace The fully qualified name for the Event Hubs namespace. This is likely to be + * similar to {@literal "{your-namespace}.servicebus.windows.net}". + * @param eventHubName The name of the Event Hub to connect the client to. + * @param credential The shared access name and key credential to use for authorization. + * Access controls may be specified by the Event Hubs namespace or the requested Event Hub, + * depending on Azure configuration. + * + * @return The updated {@link EventHubClientBuilder} object. + * @throws IllegalArgumentException if {@code fullyQualifiedNamespace} or {@code eventHubName} is an empty + * string. + * @throws NullPointerException if {@code fullyQualifiedNamespace}, {@code eventHubName}, {@code credentials} is + * null. + */ + public EventHubClientBuilder credential(String fullyQualifiedNamespace, String eventHubName, + AzureNamedKeyCredential credential) { + + this.fullyQualifiedNamespace = Objects.requireNonNull(fullyQualifiedNamespace, + "'fullyQualifiedNamespace' cannot be null."); + this.eventHubName = Objects.requireNonNull(eventHubName, "'eventHubName' cannot be null."); + + if (CoreUtils.isNullOrEmpty(fullyQualifiedNamespace)) { + throw logger.logExceptionAsError(new IllegalArgumentException("'host' cannot be an empty string.")); + } else if (CoreUtils.isNullOrEmpty(eventHubName)) { + throw logger.logExceptionAsError(new IllegalArgumentException("'eventHubName' cannot be an empty string.")); + } + + this.credentials = getTokenCredential(Objects.requireNonNull(credential, "'credential' cannot be null.")); + + return this; + } + + /** + * Sets the credential information for which Event Hub instance to connect to, and how to authorize against it. + * + * @param fullyQualifiedNamespace The fully qualified name for the Event Hubs namespace. This is likely to be + * similar to {@literal "{your-namespace}.servicebus.windows.net}". + * @param eventHubName The name of the Event Hub to connect the client to. + * @param credential The shared access signature credential to use for authorization. + * Access controls may be specified by the Event Hubs namespace or the requested Event Hub, + * depending on Azure configuration. + * + * @return The updated {@link EventHubClientBuilder} object. + * @throws IllegalArgumentException if {@code fullyQualifiedNamespace} or {@code eventHubName} is an empty + * string. + * @throws NullPointerException if {@code fullyQualifiedNamespace}, {@code eventHubName}, {@code credentials} is + * null. + */ + public EventHubClientBuilder credential(String fullyQualifiedNamespace, String eventHubName, + AzureSasCredential credential) { + + this.fullyQualifiedNamespace = Objects.requireNonNull(fullyQualifiedNamespace, + "'fullyQualifiedNamespace' cannot be null."); + this.eventHubName = Objects.requireNonNull(eventHubName, "'eventHubName' cannot be null."); + + if (CoreUtils.isNullOrEmpty(fullyQualifiedNamespace)) { + throw logger.logExceptionAsError(new IllegalArgumentException("'host' cannot be an empty string.")); + } else if (CoreUtils.isNullOrEmpty(eventHubName)) { + throw logger.logExceptionAsError(new IllegalArgumentException("'eventHubName' cannot be an empty string.")); + } + + this.credentials = getTokenCredential(Objects.requireNonNull(credential, "'credential' cannot be null.")); + + return this; + } + /** * Sets the proxy configuration to use for {@link EventHubAsyncClient}. When a proxy is configured, {@link * AmqpTransportType#AMQP_WEB_SOCKETS} must be used for the transport type. diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index 65addbcff293..e4e4d3ec2c85 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -7,6 +7,8 @@ import com.azure.core.amqp.ProxyAuthenticationType; import com.azure.core.amqp.ProxyOptions; import com.azure.core.util.Configuration; +import com.azure.core.credential.AzureNamedKeyCredential; +import com.azure.core.credential.AzureSasCredential; import com.azure.messaging.eventhubs.implementation.ClientConstants; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; @@ -125,6 +127,27 @@ public void testProxyOptionsConfiguration(String proxyConfiguration, boolean exp Assertions.assertEquals(expectedClientCreation, clientCreated); } + @Test + public void testConnectionWithAzureNameKeyCredential() { + String fullyQualifiedNamespace = "sb-name.servicebus.windows.net"; + String sharedAccessKeyName = "SharedAccessKeyName test-value"; + String sharedAccessKey = "SharedAccessKey test-value"; + String eventHubName = "test-event-hub-name"; + assertNotNull(new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, + new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey))); + } + + @Test + public void testConnectionWithAzureSasCredential() { + String fullyQualifiedNamespace = "sb-name.servicebus.windows.net"; + String sharedAccessSignature = "SharedAccessSignature test-value"; + String eventHubName = "test-event-hub-name"; + assertNotNull(new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, + new AzureSasCredential(sharedAccessSignature))); + } + private static Stream getProxyConfigurations() { return Stream.of( Arguments.of("http://localhost:8080", true), diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java deleted file mode 100644 index 94d3ccc8e4f0..000000000000 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredential.java +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.identity; - -import com.azure.core.annotation.Immutable; -import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenCredential; -import com.azure.core.credential.TokenRequestContext; -import com.azure.core.util.CoreUtils; -import com.azure.core.util.logging.ClientLogger; -import reactor.core.publisher.Mono; - -import javax.crypto.Mac; -import javax.crypto.spec.SecretKeySpec; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.time.Duration; -import java.time.Instant; -import java.time.OffsetDateTime; -import java.time.ZoneOffset; -import java.util.Arrays; -import java.util.Base64; -import java.util.Locale; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * Authorizes with Azure Event Hub service using a shared access key from either an Event Hub namespace or a specific - * Event Hub. - * - *

- * The shared access key can be obtained by creating a shared access policy for the Event Hub namespace or for - * a specific Event Hub instance. See - * Shared access authorization policies for more information. - *

- * - * @see Authorizeaccess with shared access signature. - */ -@Immutable -public class EventHubSharedKeyCredential implements TokenCredential { - - private static final String SHARED_ACCESS_SIGNATURE_FORMAT = "SharedAccessSignature sr=%s&sig=%s&se=%s&skn=%s"; - private static final String HASH_ALGORITHM = "HMACSHA256"; - public static final Duration TOKEN_VALIDITY = Duration.ofMinutes(20); - - private final String sharedAccessPolicy; - private final SecretKeySpec secretKeySpec; - private final String sharedAccessSignature; - private final ClientLogger logger = new ClientLogger(EventHubSharedKeyCredential.class); - - /** - * Creates an instance that authorizes using the {@code sharedAccessPolicy} and {@code sharedAccessKey} - * and {@code sharedAccessSignature}. - * - * @param sharedAccessPolicy Name of the shared access key policy. - * @param secretKeySpec Value of the shared access key. - * @param sharedAccessSignature Value of the shared access signature. - */ - public EventHubSharedKeyCredential(String sharedAccessPolicy, SecretKeySpec secretKeySpec, - String sharedAccessSignature) { - this.sharedAccessPolicy = sharedAccessPolicy; - this.secretKeySpec = secretKeySpec; - this.sharedAccessSignature = sharedAccessSignature; - } - - /** - * Retrieves the token, given the audience/resources requested, for use in authorization against an Event Hub - * namespace or a specific Event Hub instance. - * - * @param request The details of a token request - * @return A Mono that completes and returns the shared access signature. - * @throws IllegalArgumentException if {@code scopes} does not contain a single value, which is the token - * audience. - */ - @Override - public Mono getToken(TokenRequestContext request) { - return Mono.fromCallable(() -> generateSharedAccessSignature(request.getScopes().get(0))); - } - - private AccessToken generateSharedAccessSignature(final String resource) throws UnsupportedEncodingException { - if (CoreUtils.isNullOrEmpty(resource)) { - throw logger.logExceptionAsError(new IllegalArgumentException("resource cannot be empty")); - } - - if (sharedAccessSignature != null) { - return new AccessToken(sharedAccessSignature, getExpirationTime(sharedAccessSignature)); - } - - final Mac hmac; - try { - hmac = Mac.getInstance(HASH_ALGORITHM); - hmac.init(secretKeySpec); - } catch (NoSuchAlgorithmException e) { - throw logger.logExceptionAsError(new UnsupportedOperationException( - String.format("Unable to create hashing algorithm '%s'", HASH_ALGORITHM), e)); - } catch (InvalidKeyException e) { - throw logger.logExceptionAsError(new IllegalArgumentException( - "'sharedAccessKey' is an invalid value for the hashing algorithm.", e)); - } - - final String utf8Encoding = UTF_8.name(); - final OffsetDateTime expiresOn = OffsetDateTime.now(ZoneOffset.UTC).plus(TOKEN_VALIDITY); - final String expiresOnEpochSeconds = Long.toString(expiresOn.toEpochSecond()); - final String audienceUri = URLEncoder.encode(resource, utf8Encoding); - final String secretToSign = audienceUri + "\n" + expiresOnEpochSeconds; - - final byte[] signatureBytes = hmac.doFinal(secretToSign.getBytes(utf8Encoding)); - final String signature = Base64.getEncoder().encodeToString(signatureBytes); - - final String token = String.format(Locale.US, SHARED_ACCESS_SIGNATURE_FORMAT, - audienceUri, - URLEncoder.encode(signature, utf8Encoding), - URLEncoder.encode(expiresOnEpochSeconds, utf8Encoding), - URLEncoder.encode(sharedAccessPolicy, utf8Encoding)); - return new AccessToken(token, expiresOn); - } - - private OffsetDateTime getExpirationTime(String sharedAccessSignature) { - String[] parts = sharedAccessSignature.split("&"); - return Arrays.stream(parts) - .map(part -> part.split("=")) - .filter(pair -> pair.length == 2 && pair[0].equalsIgnoreCase("se")) - .findFirst() - .map(pair -> pair[1]) - .map(expirationTimeStr -> { - try { - long epochSeconds = Long.parseLong(expirationTimeStr); - return Instant.ofEpochSecond(epochSeconds).atOffset(ZoneOffset.UTC); - } catch (NumberFormatException exception) { - logger.verbose("Invalid expiration time format in the SAS token: {}. Falling back to max " - + "expiration time.", expirationTimeStr); - return OffsetDateTime.MAX; - } - }) - .orElse(OffsetDateTime.MAX); - } -} diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java deleted file mode 100644 index 2cea4b06e690..000000000000 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EventHubSharedKeyCredentialBuilder.java +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.identity; - -import com.azure.identity.implementation.util.ValidationUtil; -import javax.crypto.spec.SecretKeySpec; -import java.util.HashMap; -import java.util.Map; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * Fluent credential builder for instantiating a {@link EventHubSharedKeyCredential}. - * - * @see EventHubSharedKeyCredential - */ -public class EventHubSharedKeyCredentialBuilder extends AadCredentialBuilderBase { - - private static final String HASH_ALGORITHM = "HMACSHA256"; - - private String sharedAccessPolicy; - private SecretKeySpec secretKeySpec; - private String sharedAccessSignature; - - /** - * Sets the sharedAccessPolicy of the user. - * - * @param sharedAccessPolicy the sharedAccessPolicy of the user - * @return the EventHubSharedKeyCredentialBuilder itself - */ - public EventHubSharedKeyCredentialBuilder sharedAccessPolicy(String sharedAccessPolicy) { - this.sharedAccessPolicy = sharedAccessPolicy; - return this; - } - - /** - * Sets the shardAccessKey of the user. - * - * @param sharedAccessKey the sharedAccessKey of the user - * @return the ServiceBusSharedKeyCredentialBuilder itself - */ - public EventHubSharedKeyCredentialBuilder sharedAccessKey(String sharedAccessKey) { - this.secretKeySpec = new SecretKeySpec(sharedAccessKey.getBytes(UTF_8), HASH_ALGORITHM); - return this; - } - - /** - * Sets the sharedAccessSignature of the user. - * - * @param sharedAccessSignature the sharedAccessSignature of the user - * @return the ServiceBusSharedKeyCredentialBuilder itself - */ - public EventHubSharedKeyCredentialBuilder sharedAccessSignature(String sharedAccessSignature) { - this.sharedAccessSignature = sharedAccessSignature; - return this; - } - - /** - * Creates a new {@link EventHubSharedKeyCredential} with the current configurations. - * - * @return a {@link EventHubSharedKeyCredential} with the current configurations. - */ - public EventHubSharedKeyCredential build() { - Map validationMap = new HashMap(); - validationMap.put("sharedAccessPolicy", sharedAccessPolicy); - validationMap.put("sharedAccessKey", secretKeySpec); - validationMap.put("sharedAccessSignature", sharedAccessSignature); - ValidationUtil.validateForSharedAccessKey(getClass().getSimpleName(), validationMap); - return new EventHubSharedKeyCredential(sharedAccessPolicy, secretKeySpec, sharedAccessSignature); - } -} diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java index 3a46986e3aed..2ace21a1d2a8 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/util/ValidationUtil.java @@ -18,23 +18,6 @@ public final class ValidationUtil { private static Pattern tenantIdentifierCharPattern = Pattern.compile("^(?:[A-Z]|[0-9]|[a-z]|-|.)+$"); - public static void validateForSharedAccessKey(String className, Map parameters) { - ClientLogger logger = new ClientLogger(className); - List missing = new ArrayList<>(); - if (parameters.get("sharedAccessSignature") == null) { - parameters.remove("sharedAccessSignature"); - for (Map.Entry entry : parameters.entrySet()) { - if (entry.getValue() == null) { - missing.add(entry.getKey()); - } - } - if (missing.size() > 0) { - throw logger.logExceptionAsWarning(new IllegalArgumentException("Must provide non-null values for " - + String.join(", ", missing) + " properties in " + className)); - } - } - } - public static void validate(String className, Map parameters) { ClientLogger logger = new ClientLogger(className); List missing = new ArrayList<>(); diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java deleted file mode 100644 index b70584a143de..000000000000 --- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EventHubSharedKeyCredentialTest.java +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -package com.azure.identity; - -import com.azure.core.credential.AccessToken; -import com.azure.core.credential.TokenRequestContext; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import java.time.OffsetDateTime; -import java.time.ZoneOffset; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -@RunWith(PowerMockRunner.class) -@PrepareForTest(fullyQualifiedNames = "com.azure.identity.*") -@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*"}) -public class EventHubSharedKeyCredentialTest { - - private static final String SHARED_ACCESS_POLICY = ""; - - private static final String SHARED_ACCESS_KEY = ""; - - private static final String SHARED_ACCESS_SIGNATURE = ""; - - private static final TokenRequestContext REQUEST = new TokenRequestContext().addScopes(""); - - @Test - public void testValidShardAccessKey() throws Exception { - - // mock - EventHubSharedKeyCredential eventHubSharedKeyCredential = PowerMockito.mock(EventHubSharedKeyCredential.class); - when(eventHubSharedKeyCredential.getToken(any(TokenRequestContext.class))) - .thenReturn(getMockAccessToken(SHARED_ACCESS_SIGNATURE, OffsetDateTime.now(ZoneOffset.UTC))); - PowerMockito.whenNew(EventHubSharedKeyCredential.class).withAnyArguments().thenReturn(eventHubSharedKeyCredential); - - EventHubSharedKeyCredential credential - = new EventHubSharedKeyCredentialBuilder() - .sharedAccessKey(SHARED_ACCESS_KEY) - .sharedAccessPolicy(SHARED_ACCESS_POLICY) - .build(); - StepVerifier.create(credential.getToken(REQUEST)) - .expectNextMatches(accessToken -> SHARED_ACCESS_SIGNATURE.equals(accessToken.getToken())) - .verifyComplete(); - } - - @Test - public void testValidSharedAccessSignature() { - EventHubSharedKeyCredential credential - = new EventHubSharedKeyCredentialBuilder() - .sharedAccessSignature(SHARED_ACCESS_SIGNATURE) - .build(); - StepVerifier.create(credential.getToken(REQUEST)) - .expectNextMatches(accessToken -> SHARED_ACCESS_SIGNATURE.equals(accessToken.getToken())) - .verifyComplete(); - } - - /** - * Creates a mock {@link AccessToken} instance. - * @param accessToken the access token to return - * @param expiresOn the expiration time - * @return a Mono publisher of the result - */ - public static Mono getMockAccessToken(String accessToken, OffsetDateTime expiresOn) { - return Mono.just(new AccessToken(accessToken, expiresOn.plusMinutes(20))); - } - -} From 4c0f7317a9389953791b40d6a4a141e8492e6dd9 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Tue, 18 May 2021 18:28:07 +0800 Subject: [PATCH 05/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 2021051818274 by LiHong --- .../eventhubs/EventHubClientBuilder.java | 18 +-- .../eventhubs/EventHubClientBuilderTest.java | 135 +++++++++++++++++- .../eventhubs/IntegrationTestBase.java | 6 +- 3 files changed, 140 insertions(+), 19 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java b/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java index 3381154015bd..977ec5f81af0 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java @@ -204,15 +204,6 @@ private TokenCredential getTokenCredential(ConnectionStringProperties properties return tokenCredential; } - private TokenCredential getTokenCredential(AzureNamedKeyCredential credential) { - return new EventHubSharedKeyCredential(credential.getAzureNamedKey().getName(), - credential.getAzureNamedKey().getKey(), ClientConstants.TOKEN_VALIDITY); - } - - private TokenCredential getTokenCredential(AzureSasCredential credential) { - return new EventHubSharedKeyCredential(credential.getSignature()); - } - /** * Sets the client options. * @@ -380,7 +371,9 @@ public EventHubClientBuilder credential(String fullyQualifiedNamespace, String e throw logger.logExceptionAsError(new IllegalArgumentException("'eventHubName' cannot be an empty string.")); } - this.credentials = getTokenCredential(Objects.requireNonNull(credential, "'credential' cannot be null.")); + Objects.requireNonNull(credential, "'credential' cannot be null."); + this.credentials = new EventHubSharedKeyCredential(credential.getAzureNamedKey().getName(), + credential.getAzureNamedKey().getKey(), ClientConstants.TOKEN_VALIDITY); return this; } @@ -414,7 +407,8 @@ public EventHubClientBuilder credential(String fullyQualifiedNamespace, String e throw logger.logExceptionAsError(new IllegalArgumentException("'eventHubName' cannot be an empty string.")); } - this.credentials = getTokenCredential(Objects.requireNonNull(credential, "'credential' cannot be null.")); + Objects.requireNonNull(credential, "'credential' cannot be null."); + this.credentials = new EventHubSharedKeyCredential(credential.getSignature()); return this; } @@ -563,7 +557,7 @@ public EventHubConsumerClient buildConsumerClient() { * either {@link #connectionString(String)} or {@link #credential(String, String, TokenCredential)}. Or, if a * proxy is specified but the transport type is not {@link AmqpTransportType#AMQP_WEB_SOCKETS web sockets}. */ - public EventHubProducerAsyncClient buildAsyncProducerClient() { + public EventHubProducerClient buildAsyncProducerClient() { return buildAsyncClient().createProducer(); } diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index e4e4d3ec2c85..3bb50d1bf690 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -6,9 +6,11 @@ import com.azure.core.amqp.AmqpTransportType; import com.azure.core.amqp.ProxyAuthenticationType; import com.azure.core.amqp.ProxyOptions; +import com.azure.core.amqp.implementation.ConnectionStringProperties; import com.azure.core.util.Configuration; import com.azure.core.credential.AzureNamedKeyCredential; import com.azure.core.credential.AzureSasCredential; +import com.azure.core.util.logging.ClientLogger; import com.azure.messaging.eventhubs.implementation.ClientConstants; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; @@ -23,10 +25,14 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import reactor.test.StepVerifier; + import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -public class EventHubClientBuilderTest { +import static java.nio.charset.StandardCharsets.UTF_8; + +public class EventHubClientBuilderTest extends IntegrationTestBase { private static final String NAMESPACE_NAME = "dummyNamespaceName"; private static final String DEFAULT_DOMAIN_NAME = "servicebus.windows.net/"; @@ -42,6 +48,35 @@ public class EventHubClientBuilderTest { ENDPOINT, SHARED_ACCESS_KEY_NAME, SHARED_ACCESS_KEY, EVENT_HUB_NAME); private static final Proxy PROXY_ADDRESS = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, Integer.parseInt(PROXY_PORT))); + private static final String TEST_CONTENTS = "SSLorem ipsum dolor sit amet, consectetur adipiscing elit. Donec " + + "vehicula posuere lobortis. Aliquam finibus volutpat dolor, faucibus pellentesque ipsum bibendum vitae. " + + "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut sit amet " + + "urna hendrerit, dapibus justo a, sodales justo. Mauris finibus augue id pulvinar congue. Nam maximus " + + "luctus ipsum, at commodo ligula euismod ac. Phasellus vitae lacus sit amet diam porta placerat. \n" + + "Ut sodales efficitur sapien ut posuere. Morbi sed tellus est. Proin eu erat purus. Proin massa nunc, " + + "condimentum id iaculis dignissim, consectetur et odio. Cras suscipit sem eu libero aliquam tincidunt. " + + "Nullam ut arcu suscipit, eleifend velit in, cursus libero. Ut eleifend facilisis odio sit amet feugiat. " + + "Phasellus at nunc sit amet elit sagittis commodo ac in nisi. Fusce vitae aliquam quam. Integer vel nibh " + + "euismod, tempus elit vitae, pharetra est. Duis vulputate enim a elementum dignissim. Morbi dictum enim id " + + "elit scelerisque, in elementum nulla pharetra. \n" + + "Aenean aliquet aliquet condimentum. Proin dapibus dui id libero tempus feugiat. Sed commodo ligula a " + + "lectus mattis, vitae tincidunt velit auctor. Fusce quis semper dui. Phasellus eu efficitur sem. Ut non sem" + + " sit amet enim condimentum venenatis id dictum massa. Nullam sagittis lacus a neque sodales, et ultrices " + + "arcu mattis. Aliquam erat volutpat. \n" + + "Aenean fringilla quam elit, id mattis purus vestibulum nec. Praesent porta eros in dapibus molestie. " + + "Vestibulum orci libero, tincidunt et turpis eget, condimentum lobortis enim. Fusce suscipit ante et mauris" + + " consequat cursus nec laoreet lorem. Maecenas in sollicitudin diam, non tincidunt purus. Nunc mauris " + + "purus, laoreet eget interdum vitae, placerat a sapien. In mi risus, blandit eu facilisis nec, molestie " + + "suscipit leo. Pellentesque molestie urna vitae dui faucibus bibendum. \n" + + "Donec quis ipsum ultricies, imperdiet ex vel, scelerisque eros. Ut at urna arcu. Vestibulum rutrum odio " + + "dolor, vitae cursus nunc pulvinar vel. Donec accumsan sapien in malesuada tempor. Maecenas in condimentum " + + "eros. Sed vestibulum facilisis massa a iaculis. Etiam et nibh felis. Donec maximus, sem quis vestibulum " + + "gravida, turpis risus congue dolor, pharetra tincidunt lectus nisi at velit."; + + EventHubClientBuilderTest() { + super(new ClientLogger(EventHubClientBuilderTest.class)); + } + @Test public void missingConnectionString() { final EventHubClientBuilder builder = new EventHubClientBuilder(); @@ -127,15 +162,90 @@ public void testProxyOptionsConfiguration(String proxyConfiguration, boolean exp Assertions.assertEquals(expectedClientCreation, clientCreated); } + @Test + public void sendAndReceiveEventByAzureNameKeyCredential() { + ConnectionStringProperties properties = getConnectionStringProperties(); + String fullyQualifiedNamespace = getFullyQualifiedDomainName(); + String sharedAccessKeyName = properties.getSharedAccessKeyName();; + String sharedAccessKey = properties.getSharedAccessKey(); + String eventHubName = getEventHubName(); + + final EventData testData = new EventData(TEST_CONTENTS.getBytes(UTF_8)); + + EventHubProducerClient asyncProducerClient = new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, + new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) + .buildAsyncProducerClient(); + asyncProducerClient.send(testData); + + EventHubConsumerAsyncClient asyncConsumerClient = new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, + new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) + .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME) + .buildAsyncConsumerClient(); + StepVerifier.create(asyncConsumerClient.receive()) + .assertNext(consumer -> Assertions.assertEquals(consumer.getData().getBody(), TEST_CONTENTS.getBytes(UTF_8))) + .verifyComplete(); + + asyncProducerClient.close(); + asyncConsumerClient.close(); + } + + + @Test + public void sendAndReceiveEventByAzureSasCredential() { + ConnectionStringProperties properties = getConnectionStringProperties(true); + String fullyQualifiedNamespace = getFullyQualifiedDomainName(); + String sharedAccessSignature = properties.getSharedAccessSignature(); + String eventHubName = getEventHubName(); + + final EventData testData = new EventData(TEST_CONTENTS.getBytes(UTF_8)); + + EventHubProducerClient asyncProducerClient = new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, + new AzureSasCredential(sharedAccessSignature)) + .buildAsyncProducerClient(); + asyncProducerClient.send(testData); + + EventHubConsumerAsyncClient asyncConsumerClient = new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, + new AzureSasCredential(sharedAccessSignature)) + .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME) + .buildAsyncConsumerClient(); + StepVerifier.create(asyncConsumerClient.receive()) + .assertNext(consumer -> Assertions.assertEquals(consumer.getData().getBody(), TEST_CONTENTS.getBytes(UTF_8))) + .verifyComplete(); + + asyncProducerClient.close(); + asyncConsumerClient.close(); + } + @Test public void testConnectionWithAzureNameKeyCredential() { String fullyQualifiedNamespace = "sb-name.servicebus.windows.net"; String sharedAccessKeyName = "SharedAccessKeyName test-value"; String sharedAccessKey = "SharedAccessKey test-value"; String eventHubName = "test-event-hub-name"; - assertNotNull(new EventHubClientBuilder() - .credential(fullyQualifiedNamespace, eventHubName, + + assertThrows(NullPointerException.class, () -> new EventHubClientBuilder() + .credential(null, eventHubName, + new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey))); + + assertThrows(NullPointerException.class, () -> new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, null, + new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey))); + + assertThrows(IllegalArgumentException.class, () -> new EventHubClientBuilder() + .credential("", eventHubName, new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey))); + + assertThrows(IllegalArgumentException.class, () -> new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, "", + new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey))); + + assertThrows(NullPointerException.class, () -> new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, (AzureNamedKeyCredential) null)); + } @Test @@ -143,9 +253,22 @@ public void testConnectionWithAzureSasCredential() { String fullyQualifiedNamespace = "sb-name.servicebus.windows.net"; String sharedAccessSignature = "SharedAccessSignature test-value"; String eventHubName = "test-event-hub-name"; - assertNotNull(new EventHubClientBuilder() - .credential(fullyQualifiedNamespace, eventHubName, - new AzureSasCredential(sharedAccessSignature))); + + assertThrows(NullPointerException.class, () -> new EventHubClientBuilder() + .credential(null, eventHubName, new AzureSasCredential(sharedAccessSignature))); + + assertThrows(NullPointerException.class, () -> new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, null, new AzureSasCredential(sharedAccessSignature))); + + assertThrows(IllegalArgumentException.class, () -> new EventHubClientBuilder() + .credential("", eventHubName, new AzureSasCredential(sharedAccessSignature))); + + assertThrows(IllegalArgumentException.class, () -> new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, "", new AzureSasCredential(sharedAccessSignature))); + + assertThrows(NullPointerException.class, () -> new EventHubClientBuilder() + .credential(fullyQualifiedNamespace, eventHubName, (AzureSasCredential) null)); + } private static Stream getProxyConfigurations() { diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/IntegrationTestBase.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/IntegrationTestBase.java index cf70a30f1385..17c136924eb3 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/IntegrationTestBase.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/IntegrationTestBase.java @@ -220,7 +220,11 @@ protected static EventHubClientBuilder createBuilder(boolean useCredentials) { } protected static ConnectionStringProperties getConnectionStringProperties() { - return new ConnectionStringProperties(getConnectionString()); + return new ConnectionStringProperties(getConnectionString(false)); + } + + protected static ConnectionStringProperties getConnectionStringProperties(boolean withSas) { + return new ConnectionStringProperties(getConnectionString(withSas)); } /** From be6bd5534d3b075f207ec4e2d837fa026a1d415c Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Wed, 19 May 2021 09:26:52 +0800 Subject: [PATCH 06/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105190926 by LiHong --- .../eventhubs/EventHubClientBuilder.java | 2 +- .../eventhubs/EventHubClientBuilderTest.java | 35 ++++++------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java b/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java index 977ec5f81af0..1d113a58affc 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/main/java/com/azure/messaging/eventhubs/EventHubClientBuilder.java @@ -557,7 +557,7 @@ public EventHubConsumerClient buildConsumerClient() { * either {@link #connectionString(String)} or {@link #credential(String, String, TokenCredential)}. Or, if a * proxy is specified but the transport type is not {@link AmqpTransportType#AMQP_WEB_SOCKETS web sockets}. */ - public EventHubProducerClient buildAsyncProducerClient() { + public EventHubProducerAsyncClient buildAsyncProducerClient() { return buildAsyncClient().createProducer(); } diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index 3bb50d1bf690..c1fe2725be70 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -176,19 +176,13 @@ public void sendAndReceiveEventByAzureNameKeyCredential() { .credential(fullyQualifiedNamespace, eventHubName, new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) .buildAsyncProducerClient(); - asyncProducerClient.send(testData); - - EventHubConsumerAsyncClient asyncConsumerClient = new EventHubClientBuilder() - .credential(fullyQualifiedNamespace, eventHubName, - new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) - .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME) - .buildAsyncConsumerClient(); - StepVerifier.create(asyncConsumerClient.receive()) - .assertNext(consumer -> Assertions.assertEquals(consumer.getData().getBody(), TEST_CONTENTS.getBytes(UTF_8))) + try { + StepVerifier.create(asyncProducerClient.send(testData)) .verifyComplete(); + } finally { + asyncProducerClient.close(); + } - asyncProducerClient.close(); - asyncConsumerClient.close(); } @@ -205,19 +199,12 @@ public void sendAndReceiveEventByAzureSasCredential() { .credential(fullyQualifiedNamespace, eventHubName, new AzureSasCredential(sharedAccessSignature)) .buildAsyncProducerClient(); - asyncProducerClient.send(testData); - - EventHubConsumerAsyncClient asyncConsumerClient = new EventHubClientBuilder() - .credential(fullyQualifiedNamespace, eventHubName, - new AzureSasCredential(sharedAccessSignature)) - .consumerGroup(EventHubClientBuilder.DEFAULT_CONSUMER_GROUP_NAME) - .buildAsyncConsumerClient(); - StepVerifier.create(asyncConsumerClient.receive()) - .assertNext(consumer -> Assertions.assertEquals(consumer.getData().getBody(), TEST_CONTENTS.getBytes(UTF_8))) - .verifyComplete(); - - asyncProducerClient.close(); - asyncConsumerClient.close(); + try { + StepVerifier.create(asyncProducerClient.send(testData)) + .verifyComplete(); + } finally { + asyncProducerClient.close(); + } } @Test From 4f4b91e5cb410ef75be01b64be0334aefe943af4 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Wed, 19 May 2021 11:00:02 +0800 Subject: [PATCH 07/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105191059 by LiHong --- .../eventhubs/EventHubClientBuilderTest.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index c1fe2725be70..e09a98bfecc1 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -12,6 +12,8 @@ import com.azure.core.credential.AzureSasCredential; import com.azure.core.util.logging.ClientLogger; import com.azure.messaging.eventhubs.implementation.ClientConstants; + +import java.util.*; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -20,7 +22,7 @@ import java.net.Proxy; import java.net.URI; import java.net.URISyntaxException; -import java.util.Locale; + import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -172,17 +174,19 @@ public void sendAndReceiveEventByAzureNameKeyCredential() { final EventData testData = new EventData(TEST_CONTENTS.getBytes(UTF_8)); - EventHubProducerClient asyncProducerClient = new EventHubClientBuilder() + EventHubProducerAsyncClient asyncProducerClient = new EventHubClientBuilder() .credential(fullyQualifiedNamespace, eventHubName, new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) .buildAsyncProducerClient(); try { - StepVerifier.create(asyncProducerClient.send(testData)) - .verifyComplete(); + asyncProducerClient.createBatch().subscribe(batch -> { + batch.getEvents().add(testData); + StepVerifier.create(asyncProducerClient.send(batch)) + .verifyComplete(); + }); } finally { asyncProducerClient.close(); } - } @@ -195,13 +199,16 @@ public void sendAndReceiveEventByAzureSasCredential() { final EventData testData = new EventData(TEST_CONTENTS.getBytes(UTF_8)); - EventHubProducerClient asyncProducerClient = new EventHubClientBuilder() + EventHubProducerAsyncClient asyncProducerClient = new EventHubClientBuilder() .credential(fullyQualifiedNamespace, eventHubName, new AzureSasCredential(sharedAccessSignature)) .buildAsyncProducerClient(); try { - StepVerifier.create(asyncProducerClient.send(testData)) - .verifyComplete(); + asyncProducerClient.createBatch().subscribe(batch -> { + batch.getEvents().add(testData); + StepVerifier.create(asyncProducerClient.send(batch)) + .verifyComplete(); + }); } finally { asyncProducerClient.close(); } From c8811e5f2f9f3d076e1a709edade1ed085e0c9b6 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Wed, 19 May 2021 11:41:54 +0800 Subject: [PATCH 08/12] Fix issue#16466 Implement an Event Hubs Shared Access Key Credential 202105191141 by LiHong --- .../eventhubs/EventHubClientBuilderTest.java | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index e09a98bfecc1..6c3ea536e0dd 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -12,8 +12,7 @@ import com.azure.core.credential.AzureSasCredential; import com.azure.core.util.logging.ClientLogger; import com.azure.messaging.eventhubs.implementation.ClientConstants; - -import java.util.*; +import java.util.Locale; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -50,30 +49,7 @@ public class EventHubClientBuilderTest extends IntegrationTestBase { ENDPOINT, SHARED_ACCESS_KEY_NAME, SHARED_ACCESS_KEY, EVENT_HUB_NAME); private static final Proxy PROXY_ADDRESS = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, Integer.parseInt(PROXY_PORT))); - private static final String TEST_CONTENTS = "SSLorem ipsum dolor sit amet, consectetur adipiscing elit. Donec " - + "vehicula posuere lobortis. Aliquam finibus volutpat dolor, faucibus pellentesque ipsum bibendum vitae. " - + "Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut sit amet " - + "urna hendrerit, dapibus justo a, sodales justo. Mauris finibus augue id pulvinar congue. Nam maximus " - + "luctus ipsum, at commodo ligula euismod ac. Phasellus vitae lacus sit amet diam porta placerat. \n" - + "Ut sodales efficitur sapien ut posuere. Morbi sed tellus est. Proin eu erat purus. Proin massa nunc, " - + "condimentum id iaculis dignissim, consectetur et odio. Cras suscipit sem eu libero aliquam tincidunt. " - + "Nullam ut arcu suscipit, eleifend velit in, cursus libero. Ut eleifend facilisis odio sit amet feugiat. " - + "Phasellus at nunc sit amet elit sagittis commodo ac in nisi. Fusce vitae aliquam quam. Integer vel nibh " - + "euismod, tempus elit vitae, pharetra est. Duis vulputate enim a elementum dignissim. Morbi dictum enim id " - + "elit scelerisque, in elementum nulla pharetra. \n" - + "Aenean aliquet aliquet condimentum. Proin dapibus dui id libero tempus feugiat. Sed commodo ligula a " - + "lectus mattis, vitae tincidunt velit auctor. Fusce quis semper dui. Phasellus eu efficitur sem. Ut non sem" - + " sit amet enim condimentum venenatis id dictum massa. Nullam sagittis lacus a neque sodales, et ultrices " - + "arcu mattis. Aliquam erat volutpat. \n" - + "Aenean fringilla quam elit, id mattis purus vestibulum nec. Praesent porta eros in dapibus molestie. " - + "Vestibulum orci libero, tincidunt et turpis eget, condimentum lobortis enim. Fusce suscipit ante et mauris" - + " consequat cursus nec laoreet lorem. Maecenas in sollicitudin diam, non tincidunt purus. Nunc mauris " - + "purus, laoreet eget interdum vitae, placerat a sapien. In mi risus, blandit eu facilisis nec, molestie " - + "suscipit leo. Pellentesque molestie urna vitae dui faucibus bibendum. \n" - + "Donec quis ipsum ultricies, imperdiet ex vel, scelerisque eros. Ut at urna arcu. Vestibulum rutrum odio " - + "dolor, vitae cursus nunc pulvinar vel. Donec accumsan sapien in malesuada tempor. Maecenas in condimentum " - + "eros. Sed vestibulum facilisis massa a iaculis. Etiam et nibh felis. Donec maximus, sem quis vestibulum " - + "gravida, turpis risus congue dolor, pharetra tincidunt lectus nisi at velit."; + private static final String TEST_CONTENTS = "SSLorem ipsum dolor sit amet, consectetur adipiscing elit. Donec vehicula posuere lobortis. Aliquam finibus volutpat dolor, faucibus pellentesque ipsum bibendum vitae. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut sit amet urna hendrerit, dapibus justo a, sodales justo. Mauris finibus augue id pulvinar congue. Nam maximus luctus ipsum, at commodo ligula euismod ac. Phasellus vitae lacus sit amet diam porta placerat. \nUt sodales efficitur sapien ut posuere. Morbi sed tellus est. Proin eu erat purus. Proin massa nunc, condimentum id iaculis dignissim, consectetur et odio. Cras suscipit sem eu libero aliquam tincidunt. Nullam ut arcu suscipit, eleifend velit in, cursus libero. Ut eleifend facilisis odio sit amet feugiat. Phasellus at nunc sit amet elit sagittis commodo ac in nisi. Fusce vitae aliquam quam. Integer vel nibh euismod, tempus elit vitae, pharetra est. Duis vulputate enim a elementum dignissim. Morbi dictum enim id elit scelerisque, in elementum nulla pharetra. \nAenean aliquet aliquet condimentum. Proin dapibus dui id libero tempus feugiat. Sed commodo ligula a lectus mattis, vitae tincidunt velit auctor. Fusce quis semper dui. Phasellus eu efficitur sem. Ut non sem sit amet enim condimentum venenatis id dictum massa. Nullam sagittis lacus a neque sodales, et ultrices arcu mattis. Aliquam erat volutpat. \nAenean fringilla quam elit, id mattis purus vestibulum nec. Praesent porta eros in dapibus molestie. Vestibulum orci libero, tincidunt et turpis eget, condimentum lobortis enim. Fusce suscipit ante et mauris consequat cursus nec laoreet lorem. Maecenas in sollicitudin diam, non tincidunt purus. Nunc mauris purus, laoreet eget interdum vitae, placerat a sapien. In mi risus, blandit eu facilisis nec, molestie suscipit leo. Pellentesque molestie urna vitae dui faucibus bibendum. \nDonec quis ipsum ultricies, imperdiet ex vel, scelerisque eros. Ut at urna arcu. Vestibulum rutrum odio dolor, vitae cursus nunc pulvinar vel. Donec accumsan sapien in malesuada tempor. Maecenas in condimentum eros. Sed vestibulum facilisis massa a iaculis. Etiam et nibh felis. Donec maximus, sem quis vestibulum gravida, turpis risus congue dolor, pharetra tincidunt lectus nisi at velit."; EventHubClientBuilderTest() { super(new ClientLogger(EventHubClientBuilderTest.class)); From 838fd2249c8d63353bc24f12f7dd50a0cc92e6f2 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Wed, 19 May 2021 18:20:05 +0800 Subject: [PATCH 09/12] Fix ci exception for issue#16466 Implement an Event Hubs Shared Access Key Credential 202105191819 by LiHong --- .../azure/messaging/eventhubs/EventHubClientBuilderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index 6c3ea536e0dd..d411ed445023 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -144,7 +144,7 @@ public void testProxyOptionsConfiguration(String proxyConfiguration, boolean exp public void sendAndReceiveEventByAzureNameKeyCredential() { ConnectionStringProperties properties = getConnectionStringProperties(); String fullyQualifiedNamespace = getFullyQualifiedDomainName(); - String sharedAccessKeyName = properties.getSharedAccessKeyName();; + String sharedAccessKeyName = properties.getSharedAccessKeyName(); String sharedAccessKey = properties.getSharedAccessKey(); String eventHubName = getEventHubName(); From 66abe4169f4361c439a4d15a3ae9469981742e36 Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Mon, 24 May 2021 11:56:30 +0800 Subject: [PATCH 10/12] Fixed issue#16466 Implement an Event Hubs Shared Access Key Credential 202105241156 by LiHong --- .../eventhubs/EventHubClientBuilderTest.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index d411ed445023..7593bf30568f 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -28,10 +28,8 @@ import reactor.test.StepVerifier; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; - import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.*; public class EventHubClientBuilderTest extends IntegrationTestBase { private static final String NAMESPACE_NAME = "dummyNamespaceName"; @@ -154,12 +152,13 @@ public void sendAndReceiveEventByAzureNameKeyCredential() { .credential(fullyQualifiedNamespace, eventHubName, new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) .buildAsyncProducerClient(); - try { - asyncProducerClient.createBatch().subscribe(batch -> { - batch.getEvents().add(testData); - StepVerifier.create(asyncProducerClient.send(batch)) - .verifyComplete(); - }); + try{ + StepVerifier.create( + asyncProducerClient.createBatch().flatMap(batch -> { + assertTrue(batch.tryAdd(testData)); + return asyncProducerClient.send(batch); + }) + ).verifyComplete(); } finally { asyncProducerClient.close(); } @@ -179,12 +178,13 @@ public void sendAndReceiveEventByAzureSasCredential() { .credential(fullyQualifiedNamespace, eventHubName, new AzureSasCredential(sharedAccessSignature)) .buildAsyncProducerClient(); - try { - asyncProducerClient.createBatch().subscribe(batch -> { - batch.getEvents().add(testData); - StepVerifier.create(asyncProducerClient.send(batch)) - .verifyComplete(); - }); + try{ + StepVerifier.create( + asyncProducerClient.createBatch().flatMap(batch -> { + assertTrue(batch.tryAdd(testData)); + return asyncProducerClient.send(batch); + }) + ).verifyComplete(); } finally { asyncProducerClient.close(); } From 85ad3c9d67149a843ec725d82d4536d7bd2a8edc Mon Sep 17 00:00:00 2001 From: hongli750210 Date: Tue, 25 May 2021 15:07:21 +0800 Subject: [PATCH 11/12] Fixed ci exception for issue#16466 Implement an Event Hubs Shared Access Key Credential 202105251507 by LiHong --- .../azure/messaging/eventhubs/EventHubClientBuilderTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index 7593bf30568f..8333feeec693 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -152,7 +152,7 @@ public void sendAndReceiveEventByAzureNameKeyCredential() { .credential(fullyQualifiedNamespace, eventHubName, new AzureNamedKeyCredential(sharedAccessKeyName, sharedAccessKey)) .buildAsyncProducerClient(); - try{ + try { StepVerifier.create( asyncProducerClient.createBatch().flatMap(batch -> { assertTrue(batch.tryAdd(testData)); @@ -178,7 +178,7 @@ public void sendAndReceiveEventByAzureSasCredential() { .credential(fullyQualifiedNamespace, eventHubName, new AzureSasCredential(sharedAccessSignature)) .buildAsyncProducerClient(); - try{ + try { StepVerifier.create( asyncProducerClient.createBatch().flatMap(batch -> { assertTrue(batch.tryAdd(testData)); From d346f5f8f08fefb8123c209681f9b90d14d31b20 Mon Sep 17 00:00:00 2001 From: Connie Date: Wed, 9 Jun 2021 10:54:30 -0700 Subject: [PATCH 12/12] Removing * import. --- .../eventhubs/EventHubClientBuilderTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java index 8333feeec693..7f252286d5a3 100644 --- a/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java +++ b/sdk/eventhubs/azure-messaging-eventhubs/src/test/java/com/azure/messaging/eventhubs/EventHubClientBuilderTest.java @@ -7,29 +7,29 @@ import com.azure.core.amqp.ProxyAuthenticationType; import com.azure.core.amqp.ProxyOptions; import com.azure.core.amqp.implementation.ConnectionStringProperties; -import com.azure.core.util.Configuration; import com.azure.core.credential.AzureNamedKeyCredential; import com.azure.core.credential.AzureSasCredential; +import com.azure.core.util.Configuration; import com.azure.core.util.logging.ClientLogger; import com.azure.messaging.eventhubs.implementation.ClientConstants; -import java.util.Locale; -import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import reactor.test.StepVerifier; import java.net.InetSocketAddress; import java.net.Proxy; import java.net.URI; import java.net.URISyntaxException; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import reactor.test.StepVerifier; +import java.util.Locale; +import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class EventHubClientBuilderTest extends IntegrationTestBase { private static final String NAMESPACE_NAME = "dummyNamespaceName"; @@ -100,6 +100,7 @@ public void throwsWithProxyWhenTransportTypeNotChanged() { assertNotNull(builder.buildAsyncClient()); }); } + @Test public void testConnectionStringWithSas() { @@ -164,7 +165,6 @@ public void sendAndReceiveEventByAzureNameKeyCredential() { } } - @Test public void sendAndReceiveEventByAzureSasCredential() { ConnectionStringProperties properties = getConnectionStringProperties(true);