diff --git a/sdk/core/azure-core/src/main/java/com/azure/core/util/Configuration.java b/sdk/core/azure-core/src/main/java/com/azure/core/util/Configuration.java index bf7b8cbd4a8e9..677ca5ec70d24 100644 --- a/sdk/core/azure-core/src/main/java/com/azure/core/util/Configuration.java +++ b/sdk/core/azure-core/src/main/java/com/azure/core/util/Configuration.java @@ -75,6 +75,11 @@ public class Configuration implements Cloneable { */ public static final String PROPERTY_AZURE_TENANT_ID = "AZURE_TENANT_ID"; + /** + * Path of a PEM certificate file to use when performing service principal authentication with Azure. + */ + public static final String PROPERTY_AZURE_CLIENT_CERTIFICATE_PATH = "AZURE_CLIENT_CERTIFICATE_PATH"; + /** * Name of the Azure resource group. */ @@ -83,7 +88,7 @@ public class Configuration implements Cloneable { /** * Name of the Azure cloud to connect to. */ - public static final String PROPERTY_AZURE_CLOUD = "AZURE_CLOUD"; + public static final String PROPERTY_AZURE_AUTHORITY_HOST = "AZURE_AUTHORITY_HOST"; /** * Disables telemetry collection. @@ -115,8 +120,9 @@ public class Configuration implements Cloneable { PROPERTY_AZURE_CLIENT_ID, PROPERTY_AZURE_CLIENT_SECRET, PROPERTY_AZURE_TENANT_ID, + PROPERTY_AZURE_CLIENT_CERTIFICATE_PATH, PROPERTY_AZURE_RESOURCE_GROUP, - PROPERTY_AZURE_CLOUD, + PROPERTY_AZURE_AUTHORITY_HOST, PROPERTY_AZURE_TELEMETRY_DISABLED, PROPERTY_AZURE_LOG_LEVEL, PROPERTY_AZURE_TRACING_DISABLED, diff --git a/sdk/eventhubs/microsoft-azure-eventhubs-eph/Readme.md b/sdk/eventhubs/microsoft-azure-eventhubs-eph/README.md similarity index 100% rename from sdk/eventhubs/microsoft-azure-eventhubs-eph/Readme.md rename to sdk/eventhubs/microsoft-azure-eventhubs-eph/README.md diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java index 1c1f3ef6d1756..bbc795f5f3687 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/EnvironmentCredential.java @@ -14,7 +14,7 @@ import reactor.core.publisher.Mono; /** - * A credential provider that provides token credentials based on environment variables. The environment variables + * A credential provider that provides token credentials based on environment variables. The sets of environment variables * expected are: *

*

+ * or: + *

+ *

+ * or: + *

+ *

*/ @Immutable public class EnvironmentCredential implements TokenCredential { @@ -41,15 +55,22 @@ public class EnvironmentCredential implements TokenCredential { @Override public Mono getToken(TokenRequestContext request) { + String clientId = configuration.get(Configuration.PROPERTY_AZURE_CLIENT_ID); + String tenantId = configuration.get(Configuration.PROPERTY_AZURE_TENANT_ID); + String clientSecret = configuration.get(Configuration.PROPERTY_AZURE_CLIENT_SECRET); + String certPath = configuration.get(Configuration.PROPERTY_AZURE_CLIENT_CERTIFICATE_PATH); + String username = configuration.get(Configuration.PROPERTY_AZURE_USERNAME); + String password = configuration.get(Configuration.PROPERTY_AZURE_PASSWORD); return Mono.fromSupplier(() -> { - if (configuration.contains(Configuration.PROPERTY_AZURE_CLIENT_ID) - && configuration.contains(Configuration.PROPERTY_AZURE_CLIENT_SECRET) - && configuration.contains(Configuration.PROPERTY_AZURE_TENANT_ID)) { - // TODO: support other clouds - return new ClientSecretCredential(configuration.get(Configuration.PROPERTY_AZURE_TENANT_ID), - configuration.get(Configuration.PROPERTY_AZURE_CLIENT_ID), - configuration.get(Configuration.PROPERTY_AZURE_CLIENT_SECRET), - identityClientOptions); + if (verifyNotNull(clientId)) { + if (verifyNotNull(tenantId, clientSecret)) { + // TODO: support other clouds + return new ClientSecretCredential(tenantId, clientId, clientSecret, identityClientOptions); + } else if (verifyNotNull(tenantId, certPath)) { + return new ClientCertificateCredential(tenantId, clientId, certPath, null, identityClientOptions); + } else if (verifyNotNull(username, password)) { + return new UsernamePasswordCredential(clientId, tenantId, username, password, identityClientOptions); + } } // Other environment variables @@ -58,4 +79,13 @@ public Mono getToken(TokenRequestContext request) { null)); }).flatMap(cred -> cred.getToken(request)); } + + private boolean verifyNotNull(String... configs){ + for(String config: configs){ + if(config == null){ + return false; + } + } + return true; + } } diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java index 33cad876b36b3..454e3bef413b6 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClient.java @@ -5,6 +5,7 @@ import com.azure.core.credential.AccessToken; import com.azure.core.credential.TokenRequestContext; +import com.azure.core.exception.ClientAuthenticationException; import com.azure.core.http.ProxyOptions; import com.azure.core.util.serializer.SerializerAdapter; import com.azure.core.util.serializer.SerializerEncoding; diff --git a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java index 4fb5c1372ab10..5205bd2f13563 100644 --- a/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java +++ b/sdk/identity/azure-identity/src/main/java/com/azure/identity/implementation/IdentityClientOptions.java @@ -24,7 +24,9 @@ public final class IdentityClientOptions { * Creates an instance of IdentityClientOptions with default settings. */ public IdentityClientOptions() { - authorityHost = DEFAULT_AUTHORITY_HOST; + Configuration configuration = Configuration.getGlobalConfiguration(); + authorityHost = configuration.contains(configuration.PROPERTY_AZURE_AUTHORITY_HOST) + ? configuration.get(configuration.PROPERTY_AZURE_AUTHORITY_HOST) : DEFAULT_AUTHORITY_HOST; maxRetry = MAX_RETRY_DEFAULT_LIMIT; retryTimeout = i -> Duration.ofSeconds((long) Math.pow(2, i.getSeconds() - 1)); } diff --git a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EnvironmentCredentialTests.java b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EnvironmentCredentialTests.java index 7c3e54c1a7eab..5483db061fe04 100644 --- a/sdk/identity/azure-identity/src/test/java/com/azure/identity/EnvironmentCredentialTests.java +++ b/sdk/identity/azure-identity/src/test/java/com/azure/identity/EnvironmentCredentialTests.java @@ -17,11 +17,11 @@ public class EnvironmentCredentialTests { @Test - public void testCreateEnvironmentCredential() { - Configuration configuration = Configuration.getGlobalConfiguration(); - configuration.put(Configuration.PROPERTY_AZURE_CLIENT_ID, "foo"); - configuration.put(Configuration.PROPERTY_AZURE_CLIENT_SECRET, "bar"); - configuration.put(Configuration.PROPERTY_AZURE_TENANT_ID, "baz"); + public void testCreateEnvironmentClientSecretCredential() { + Configuration.getGlobalConfiguration() + .put(Configuration.PROPERTY_AZURE_CLIENT_ID, "foo") + .put(Configuration.PROPERTY_AZURE_USERNAME, "bar") + .put(Configuration.PROPERTY_AZURE_PASSWORD, "baz"); EnvironmentCredential credential = new EnvironmentCredentialBuilder().build(); @@ -36,4 +36,46 @@ public void testCreateEnvironmentCredential() { .expectNextMatches(token -> "token".equals(token.getToken())) .verifyComplete(); } + + @Test + public void testCreateEnvironmentClientCertificateCredential() { + Configuration.getGlobalConfiguration() + .put(Configuration.PROPERTY_AZURE_CLIENT_ID, "foo") + .put(Configuration.PROPERTY_AZURE_USERNAME, "bar") + .put(Configuration.PROPERTY_AZURE_PASSWORD, "baz"); + + EnvironmentCredential credential = new EnvironmentCredentialBuilder().build(); + + // authentication will fail client-id=foo, but should be able to create ClientCertificateCredential + StepVerifier.create(credential.getToken(new TokenRequestContext().addScopes("qux/.default")) + .doOnSuccess(s -> fail()) + .onErrorResume(t -> { + String message = t.getMessage(); + Assert.assertFalse(message != null && message.contains("Cannot create any credentials with the current environment variables")); + return Mono.just(new AccessToken("token", OffsetDateTime.MAX)); + })) + .expectNextMatches(token -> "token".equals(token.getToken())) + .verifyComplete(); + } + + @Test + public void testCreateEnvironmentUserPasswordCredential() { + Configuration.getGlobalConfiguration() + .put(Configuration.PROPERTY_AZURE_CLIENT_ID, "foo") + .put(Configuration.PROPERTY_AZURE_USERNAME, "bar") + .put(Configuration.PROPERTY_AZURE_PASSWORD, "baz"); + + EnvironmentCredential credential = new EnvironmentCredentialBuilder().build(); + + // authentication will fail client-id=foo, but should be able to create UsernamePasswordCredential + StepVerifier.create(credential.getToken(new TokenRequestContext().addScopes("qux/.default")) + .doOnSuccess(s -> fail()) + .onErrorResume(t -> { + String message = t.getMessage(); + Assert.assertFalse(message != null && message.contains("Cannot create any credentials with the current environment variables")); + return Mono.just(new AccessToken("token", OffsetDateTime.MAX)); + })) + .expectNextMatches(token -> "token".equals(token.getToken())) + .verifyComplete(); + } }