-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Storage identity remove resource manager provider #15837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 35 commits
03f8ab4
7266ac5
0de9b40
3964b68
9fb49ba
9acd2b9
bedcf50
9ad8a79
f9adbe0
9cac0d3
c2bbb46
41f32fa
8c1297b
f0322af
8a6385c
9305ee1
92fd3dd
06d8aa9
723e463
62c5533
b96c739
3f7e8ac
670e4a2
cfef365
f7f9414
9662b85
6f780a9
ef3f807
525c5b4
7d7adb1
80a9e20
9277d71
e4608d5
42d5e50
29beabc
969353e
bbc811e
01b1378
cc74b40
cdb5ed4
bccd6e2
9a206f1
f19af87
7bec935
456d383
9b1792b
fcb8bc8
a6cfcfe
8277c89
005e67f
552af0c
2456b26
8558375
dde0594
2a7d5a3
5531c84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
|
|
||
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>com.azure</groupId> | ||
| <artifactId>azure-spring-boot-service</artifactId> | ||
| <version>1.0.0</version> | ||
| </parent> | ||
| <artifactId>azure-identity-spring-library</artifactId> | ||
| <packaging>jar</packaging> | ||
| <name>Azure Identity Spring Integration Library</name> | ||
| <version>1.0.0-beta.1</version> | ||
| <properties> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| <maven.compiler.source>1.8</maven.compiler.source> | ||
| <maven.compiler.target>1.8</maven.compiler.target> | ||
| </properties> | ||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-surefire-plugin</artifactId> | ||
| <version>3.0.0-M5</version> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>org.springframework</groupId> | ||
| <artifactId>spring-context</artifactId> | ||
| <version>5.2.8.RELEASE</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework</groupId> | ||
| <artifactId>spring-core</artifactId> | ||
| <version>5.2.8.RELEASE</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.azure</groupId> | ||
| <artifactId>azure-identity</artifactId> | ||
| <version>1.0.9</version> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-api</artifactId> | ||
| <version>5.6.2</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-params</artifactId> | ||
| <version>5.6.2</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter-engine</artifactId> | ||
| <version>5.6.2</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>junit</groupId> | ||
| <artifactId>junit</artifactId> | ||
| <scope>test</scope> | ||
| <version>4.13</version> | ||
| </dependency> | ||
| </dependencies> | ||
| </project> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,198 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
| package com.microsoft.azure.identity.spring; | ||
|
|
||
| import java.util.HashMap; | ||
| import java.util.Optional; | ||
|
|
||
| import org.springframework.core.env.Environment; | ||
|
|
||
| import com.azure.core.credential.TokenCredential; | ||
| import com.azure.identity.ChainedTokenCredential; | ||
| import com.azure.identity.ChainedTokenCredentialBuilder; | ||
| import com.azure.identity.ClientCertificateCredentialBuilder; | ||
| import com.azure.identity.ClientSecretCredentialBuilder; | ||
| import com.azure.identity.DefaultAzureCredentialBuilder; | ||
|
|
||
| /** | ||
| * A helper class to deal with credentials in a Spring environment. | ||
| * | ||
| * <p> | ||
| * This helper class makes it possible to configure credentials to be used | ||
| * within a Spring context. | ||
| * </p> | ||
| * | ||
| * <table> | ||
| * <tr> | ||
| * <th>Property Tuples</th> | ||
| * <th>Description</th> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>azure.credential.(name.)tenantId <br> | ||
| * azure.credential.(name.)clientId <br> | ||
| * azure.credential.(name.)clientSecret</td> | ||
| * <td>the Azure Tenant ID <br> | ||
| * the Client ID <br> | ||
| * the Client Certificate <br> | ||
| * </td> | ||
| * </tr> | ||
| * <tr> | ||
| * <td>azure.credential.(name.)tenantId <br> | ||
| * azure.credential.(name.)clientId <br> | ||
| * azure.credential.(name.)clientCertificate</td> | ||
| * <td>the Azure Tenant ID <br> | ||
| * the Client ID <br> | ||
| * the path to the PEM client certificate</td> | ||
| * </tr> | ||
| * </table> | ||
| * | ||
| * where name is the <code>name</code> of the credential. Note if | ||
| * <code>name</code> is entirely omitted it is taken to be the default | ||
| * credential. Note if the default credential is omitted it is configure to use | ||
| * AzureDefaultCredential which allows for the use a Managed Identity (if it is | ||
| * present). | ||
| * | ||
| * @author manfred.riem@microsoft.com | ||
| */ | ||
| public class SpringEnvironmentTokenBuilder { | ||
|
|
||
| /** | ||
| * Defines the AZURE_CREDENTIAL_PREFIX. | ||
| */ | ||
| private static final String AZURE_CREDENTIAL_PREFIX = "azure.credential."; | ||
|
|
||
| /** | ||
| * Stores the named credentials. | ||
| */ | ||
| private final HashMap<String, TokenCredential> credentials; | ||
|
|
||
| /** | ||
| * Stores the name of the credential to be returned. If omitted, the default | ||
| * credential will be returned. | ||
| */ | ||
| private String name = ""; | ||
|
|
||
| /** | ||
| * Constructor. | ||
| */ | ||
| public SpringEnvironmentTokenBuilder() { | ||
| credentials = new HashMap<>(); | ||
| credentials.put("", new DefaultAzureCredentialBuilder().build()); | ||
| } | ||
|
|
||
| /** | ||
| * Populate from Environment. | ||
| * | ||
| * @param environment the environment. | ||
| */ | ||
| public SpringEnvironmentTokenBuilder fromEnvironment(Environment environment) { | ||
| populateNamedCredential(environment, ""); | ||
| String credentialNamesKey = AZURE_CREDENTIAL_PREFIX + "names"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to consider make use of @ConfigurationProperties and model different credentials as Map, so that we don't need the 'names' here.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a repackaging of @mnriem's code, so perhaps he can answer better. My understanding is that a custom map is needed because property values could come from multiple sources, and this serves to consolidate them in a single place.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The 'names' defines the order of the chain. |
||
| if (environment.containsProperty(credentialNamesKey)) { | ||
| String[] credentialNames = environment.getProperty(credentialNamesKey).split(","); | ||
| for (int i = 0; i < credentialNames.length; i++) { | ||
| populateNamedCredential(environment, credentialNames[i]); | ||
| } | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets a credential to override a named credential. If this credential fails to produce a token, | ||
| * the original token credential will be used. | ||
| * | ||
| * @param name | ||
| * @param credential | ||
| * @return | ||
| */ | ||
| public SpringEnvironmentTokenBuilder overrideNamedCredential(String name, TokenCredential credential) { | ||
| TokenCredential currentCredential = credentials.get(name); | ||
| if (currentCredential == null) { | ||
| credentials.put(name, credential); | ||
| } else { | ||
| ChainedTokenCredentialBuilder builder = new ChainedTokenCredentialBuilder(); | ||
| builder.addFirst(credential); | ||
| builder.addLast(currentCredential); | ||
| credentials.put(name, builder.build()); | ||
| } | ||
|
|
||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Populate a named credential. | ||
| * | ||
| * @param environment the environment | ||
| * @param name the name. | ||
| */ | ||
| private void populateNamedCredential(Environment environment, String name) { | ||
| String standardizedName = name; | ||
|
|
||
| if (!standardizedName.equals("") && !standardizedName.endsWith(".")) { | ||
| standardizedName = standardizedName + "."; | ||
| } | ||
|
|
||
| String tenantIdKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "tenantId"; | ||
| String clientIdKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "clientId"; | ||
| String clientSecretKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "clientSecret"; | ||
|
|
||
| String tenantId = environment.getProperty(tenantIdKey); | ||
| String clientId = environment.getProperty(clientIdKey); | ||
| String clientSecret = environment.getProperty(clientSecretKey); | ||
|
|
||
| if (tenantId != null && clientId != null && clientSecret != null) { | ||
| TokenCredential credential = new ClientSecretCredentialBuilder().tenantId(tenantId).clientId(clientId) | ||
| .clientSecret(clientSecret).build(); | ||
| credentials.put(name, credential); | ||
| return; | ||
| } | ||
|
|
||
| String clientCertificateKey = AZURE_CREDENTIAL_PREFIX + standardizedName + "clientCertificate"; | ||
| String clientCertificatePath = environment.getProperty(clientCertificateKey); | ||
|
|
||
| if (tenantId != null && clientId != null && clientCertificatePath != null) { | ||
| TokenCredential credential = new ClientCertificateCredentialBuilder().tenantId(tenantId).clientId(clientId) | ||
| .pemCertificate(clientCertificatePath).build(); | ||
| credentials.put(name, credential); | ||
| return; | ||
| } | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For system assigned identity, tenant id is needed. Code here has some problems work with system assigned identity and user assigned identity.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be simpler to always fall back to DefaultAzureCredentialBuilder?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I defer to @mnriem on this.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For system assigned you do not need tenant id. User-assigned was not targeted and would need to be added if required. |
||
| if (!name.equals("")) { | ||
| throw new IllegalStateException("Configuration for azure.credential." + name + " is incomplete"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Sets the builder to return a credential named <code>name</code> | ||
| * | ||
| * @param name | ||
| * @return | ||
| */ | ||
| public SpringEnvironmentTokenBuilder namedCredential(String name) { | ||
| this.name = name; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Sets the builder to return the default credential. | ||
| */ | ||
| public SpringEnvironmentTokenBuilder defaultCredential() { | ||
| return namedCredential(""); | ||
| } | ||
|
|
||
| /** | ||
| * Builds an Azure TokenCredendial. | ||
| * | ||
| * @throws IllegalArgumentException if attempting to retrieve a named credential | ||
| * not defined in the environment. | ||
| */ | ||
| public TokenCredential build() { | ||
| TokenCredential result = credentials.get(name); | ||
| if (result == null) { | ||
| throw new IllegalArgumentException( | ||
| "Attempting to retrieve Azure credential not configured in the environment. (name=" + name + ")"); | ||
| } else { | ||
| return result; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,88 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
| package com.microsoft.azure.identity.spring; | ||
|
|
||
| import com.azure.identity.ClientSecretCredential; | ||
| import com.azure.identity.DefaultAzureCredential; | ||
| import com.azure.identity.DefaultAzureCredentialBuilder; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNotNull; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
| import static org.junit.jupiter.api.Assertions.fail; | ||
| import static org.junit.jupiter.api.Assertions.assertNotEquals; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.core.env.StandardEnvironment; | ||
|
|
||
| /** | ||
| * The unit tests for the AzureIdentitySpringHelper class. | ||
| * | ||
| * @author manfred.riem@microsoft.com | ||
| */ | ||
| public class SpringEnvironmentTokenBuilderTest { | ||
|
|
||
| /** | ||
| * Test getDefaultCredential method. | ||
| */ | ||
| @Test | ||
| public void testGetDefaultCredential() { | ||
| SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); | ||
| assertNotNull(builder.build()); | ||
| assertEquals(builder.build(), builder.defaultCredential().build()); | ||
| } | ||
|
|
||
| /** | ||
| * Test populate method. | ||
| */ | ||
| @Test | ||
| public void testPopulate() { | ||
| System.setProperty("azure.credential.names", ""); | ||
| System.setProperty("azure.credential.tenantId", "tenantId"); | ||
| System.setProperty("azure.credential.clientId", "clientId"); | ||
| System.setProperty("azure.credential.clientSecret", "clientSecret"); | ||
| StandardEnvironment environment = new StandardEnvironment(); | ||
| SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); | ||
| builder.fromEnvironment(environment); | ||
|
|
||
| assertNotNull(builder.build()); | ||
| assertTrue(builder.build() instanceof ClientSecretCredential); | ||
| assertEquals(builder.build(), builder.defaultCredential().build()); | ||
| } | ||
|
|
||
| /** | ||
| * Test populate method. | ||
| */ | ||
| @Test | ||
| public void testPopulate2() { | ||
| System.setProperty("azure.credential.names", "myname"); | ||
| System.setProperty("azure.credential.myname.tenantId", "tenantId"); | ||
| System.setProperty("azure.credential.myname.clientId", "clientId"); | ||
| System.setProperty("azure.credential.myname.clientSecret", "clientSecret"); | ||
| StandardEnvironment environment = new StandardEnvironment(); | ||
| SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); | ||
| builder.fromEnvironment(environment); | ||
| assertNotNull(builder.namedCredential("myname").build()); | ||
| assertTrue(builder.build() instanceof ClientSecretCredential); | ||
| assertNotEquals(builder.build(), builder.defaultCredential().build()); | ||
| } | ||
|
|
||
| /** | ||
| * Test populate method. | ||
| */ | ||
| @Test | ||
| public void testPopulate3() { | ||
| System.setProperty("azure.credential.names", "myname2"); | ||
| System.setProperty("azure.credential.myname2.tenantId", "tenantId"); | ||
| System.setProperty("azure.credential.myname2.clientSecret", "clientSecret"); | ||
| StandardEnvironment environment = new StandardEnvironment(); | ||
| SpringEnvironmentTokenBuilder builder = new SpringEnvironmentTokenBuilder(); | ||
| try { | ||
| builder.fromEnvironment(environment); | ||
| fail(); | ||
| } catch (Throwable t) { | ||
| assertEquals(IllegalStateException.class, t.getClass(), | ||
| "Unexpected exception class on missing configuration field."); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use assertThrows() here.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I prefer not to use Exception annotations for generic exceptions (e.g. |
||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest change the artifact id to azure-identity-spring
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mnriem, any objection to eliminating the Spring Environment identity library entirely and moving this functionality to the
azure-spring-cloud-contextmodule?