Skip to content

Commit 3f37dc3

Browse files
Feature: Add the possibility to define SP Credential scope via env/system properties (#331)
1 parent a978a5e commit 3f37dc3

8 files changed

+112
-9
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ User or System Assigned Managed Identity:
5757

5858
```bash
5959
-Djenkins.azure-keyvault.uami.enabled=true
60+
-Djenkins.azure-keyvault.sp.scope=(GLOBAL|SYSTEM) # defaults to global
6061
```
6162

6263
Service principal:
@@ -66,6 +67,7 @@ Service principal:
6667
-Djenkins.azure-keyvault.sp.client_secret=...
6768
-Djenkins.azure-keyvault.sp.subscription_id=...
6869
-Djenkins.azure-keyvault.sp.tenant_id=...
70+
-Djenkins.azure-keyvault.sp.scope=(GLOBAL|SYSTEM) # defaults to global
6971
```
7072

7173
### Via environment variables
@@ -80,6 +82,7 @@ User or System Assigned Managed Identity:
8082

8183
```bash
8284
AZURE_KEYVAULT_UAMI_ENABLED=true
85+
AZURE_KEYVAULT_SP_SCOPE=(GLOBAL|SYSTEM) # defaults to global
8386
```
8487

8588
Service principal:
@@ -89,6 +92,7 @@ AZURE_KEYVAULT_SP_CLIENT_ID=...
8992
AZURE_KEYVAULT_SP_CLIENT_SECRET=...
9093
AZURE_KEYVAULT_SP_SUBSCRIPTION_ID=...
9194
AZURE_KEYVAULT_SP_TENANT_ID=...
95+
AZURE_KEYVAULT_SP_SCOPE=(GLOBAL|SYSTEM) # defaults to global
9296
```
9397

9498
## Passing a Secret File
@@ -99,13 +103,15 @@ It's possible to pass the secret from a file instead of passing the client secre
99103
-Djenkins.azure-keyvault.sp.client_secret_file=/path/to/secret/secretFile
100104
-Djenkins.azure-keyvault.sp.subscription_id=...
101105
-Djenkins.azure-keyvault.sp.tenant_id=...
106+
-Djenkins.azure-keyvault.sp.scope=(GLOBAL|SYSTEM) # defaults to global
102107
```
103108

104109
```bash
105110
AZURE_KEYVAULT_SP_CLIENT_ID=...
106111
AZURE_KEYVAULT_SP_CLIENT_SECRET_FILE=/path/to/secret/secretFile
107112
AZURE_KEYVAULT_SP_SUBSCRIPTION_ID=...
108113
AZURE_KEYVAULT_SP_TENANT_ID=...
114+
AZURE_KEYVAULT_SP_SCOPE=(GLOBAL|SYSTEM) # defaults to global
109115
```
110116

111117
The plugin will parse the contents of the file as is. The file should only contain the client_secret value.

src/main/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfiguration.java

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.logging.Logger;
2525
import jenkins.model.GlobalConfiguration;
2626
import jenkins.model.Jenkins;
27+
import org.apache.commons.lang3.ObjectUtils;
2728
import org.apache.commons.lang3.StringUtils;
2829
import org.jenkinsci.Symbol;
2930
import org.kohsuke.stapler.AncestorInPath;
@@ -97,18 +98,23 @@ private Optional<String> resolveCredentialIdFromEnvironment() {
9798
.filter(credentials -> (credentials instanceof AzureCredentials || credentials instanceof AzureImdsCredentials) && ((IdCredentials) credentials).getId().equals(GENERATED_ID))
9899
.findAny();
99100

100-
String uami = getPropertyByEnvOrSystemProperty("AZURE_KEYVAULT_UAMI_ENABLED", "jenkins.azure-keyvault.uami.enabled")
101-
.orElse("false");
101+
boolean isUami = getPropertyByEnvOrSystemProperty("AZURE_KEYVAULT_UAMI_ENABLED", "jenkins.azure-keyvault.uami.enabled")
102+
.map(u -> "true".equals(u))
103+
.orElse(false);
104+
105+
CredentialsScope scope = getPropertyByEnvOrSystemProperty("AZURE_KEYVAULT_SP_SCOPE", "jenkins.azure-keyvault.sp.scope")
106+
.map(s -> "SYSTEM".equalsIgnoreCase(s) ? CredentialsScope.SYSTEM : CredentialsScope.GLOBAL)
107+
.orElse(CredentialsScope.GLOBAL);
102108

103109
AzureBaseCredentials credentials;
104-
if (uami.equals("true")) {
105-
if (optionalCredentials.isPresent() && optionalCredentials.get() instanceof AzureImdsCredentials) {
110+
if (isUami) {
111+
if (optionalCredentials.filter(c -> c instanceof AzureImdsCredentials && scope.equals(c.getScope())).isPresent()) {
106112
// don't overwrite the credential if it matches what we currently have so as we don't save to disk all the time
107113
return Optional.empty();
108114
}
109115

110116
credentials = new AzureImdsCredentials(
111-
CredentialsScope.GLOBAL, GENERATED_ID, GENERATED_DESCRIPTION
117+
scope, GENERATED_ID, GENERATED_DESCRIPTION
112118
);
113119
storeCredential(credentials);
114120
return Optional.of(credentials.getId());
@@ -134,24 +140,26 @@ private Optional<String> resolveCredentialIdFromEnvironment() {
134140
clientId,
135141
clientSecret,
136142
subscriptionId,
137-
tenantId)
143+
tenantId,
144+
scope)
138145
) {
139146
// don't overwrite the credential if it matches what we currently have so as we don't save to disk all the time
140147
return Optional.empty();
141148
}
142149

143-
AzureCredentials azureCredentials = new AzureCredentials(CredentialsScope.GLOBAL, GENERATED_ID, GENERATED_DESCRIPTION, subscriptionId, clientId, clientSecret);
150+
AzureCredentials azureCredentials = new AzureCredentials(scope, GENERATED_ID, GENERATED_DESCRIPTION, subscriptionId, clientId, clientSecret);
144151
azureCredentials.setTenant(tenantId);
145152

146153
storeCredential(azureCredentials);
147154
return Optional.of(azureCredentials.getId());
148155
}
149156

150-
private boolean azureCredentialIsEqual(AzureCredentials creds, String clientId, String clientSecret, String subscriptionId, String tenantId) {
157+
private boolean azureCredentialIsEqual(AzureCredentials creds, String clientId, String clientSecret, String subscriptionId, String tenantId, CredentialsScope scope) {
151158
return StringUtils.equals(creds.getClientId(), clientId) &&
152159
StringUtils.equals(creds.getPlainClientSecret(), clientSecret) &&
153160
StringUtils.equals(creds.getSubscriptionId(), subscriptionId) &&
154-
StringUtils.equals(creds.getTenant(), tenantId);
161+
StringUtils.equals(creds.getTenant(), tenantId) &&
162+
ObjectUtils.equals(creds.getScope(), scope);
155163
}
156164

157165
private Optional<String> getPropertyByEnvOrSystemProperty(String envVariable, String systemProperty) {

src/test/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfigurationEnvVarSPSecretFileTest.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jenkinsci.plugins.azurekeyvaultplugin;
22

33
import com.cloudbees.plugins.credentials.Credentials;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
45
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
56
import com.microsoft.azure.util.AzureCredentials;
67
import org.junit.jupiter.api.Test;
@@ -37,5 +38,23 @@ void testValuesSet(JenkinsRule j) {
3738
assertThat(azureCredentials.getPlainClientSecret(), is("1255534"));
3839
assertThat(azureCredentials.getSubscriptionId(), is("5678"));
3940
assertThat(azureCredentials.getTenant(), is("tenant_id"));
41+
assertThat(azureCredentials.getScope(), is(CredentialsScope.GLOBAL));
42+
}
43+
44+
@Test
45+
@SetEnvironmentVariable(key = "AZURE_KEYVAULT_SP_SCOPE", value = "SYSTEM")
46+
void testValuesSetWithScope(JenkinsRule j) {
47+
AzureKeyVaultGlobalConfiguration configuration = AzureKeyVaultGlobalConfiguration.get();
48+
49+
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
50+
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
51+
assertThat(configuration.getKeyVaultURL(), is("https://mine.vault.azure.net"));
52+
53+
Credentials credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
54+
55+
assertThat(credentials, instanceOf(AzureCredentials.class));
56+
AzureCredentials azureCredentials = (AzureCredentials) credentials;
57+
58+
assertThat(azureCredentials.getScope(), is(CredentialsScope.SYSTEM));
4059
}
4160
}

src/test/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfigurationEnvVarSPTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jenkinsci.plugins.azurekeyvaultplugin;
22

33
import com.cloudbees.plugins.credentials.Credentials;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
45
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
56
import com.microsoft.azure.util.AzureCredentials;
67
import org.junit.jupiter.api.Test;
@@ -37,5 +38,27 @@ void testValuesSet(JenkinsRule j) {
3738
assertThat(azureCredentials.getPlainClientSecret(), is("1255534"));
3839
assertThat(azureCredentials.getSubscriptionId(), is("5678"));
3940
assertThat(azureCredentials.getTenant(), is("tenant_id"));
41+
assertThat(azureCredentials.getScope(), is(CredentialsScope.GLOBAL));
42+
}
43+
44+
@Test
45+
@SetEnvironmentVariable(key = "AZURE_KEYVAULT_SP_SCOPE", value = "SYSTEM")
46+
void testValuesSetWithScope(JenkinsRule j) {
47+
AzureKeyVaultGlobalConfiguration configuration = AzureKeyVaultGlobalConfiguration.get();
48+
49+
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
50+
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
51+
assertThat(configuration.getKeyVaultURL(), is("https://mine.vault.azure.net"));
52+
53+
Credentials credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
54+
55+
assertThat(credentials, instanceOf(AzureCredentials.class));
56+
AzureCredentials azureCredentials = (AzureCredentials) credentials;
57+
58+
assertThat(azureCredentials.getClientId(), is("1234"));
59+
assertThat(azureCredentials.getPlainClientSecret(), is("1255534"));
60+
assertThat(azureCredentials.getSubscriptionId(), is("5678"));
61+
assertThat(azureCredentials.getTenant(), is("tenant_id"));
62+
assertThat(azureCredentials.getScope(), is(CredentialsScope.SYSTEM));
4063
}
4164
}

src/test/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfigurationEnvVarUamiTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jenkinsci.plugins.azurekeyvaultplugin;
22

33
import com.cloudbees.plugins.credentials.Credentials;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
45
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
56
import com.microsoft.azure.util.AzureImdsCredentials;
67
import org.junit.jupiter.api.Test;
@@ -27,5 +28,20 @@ void testValuesSet(JenkinsRule j) {
2728
Credentials credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
2829

2930
assertThat(credentials, instanceOf(AzureImdsCredentials.class));
31+
assertThat(credentials.getScope(), is(CredentialsScope.GLOBAL));
32+
}
33+
34+
@Test
35+
@SetEnvironmentVariable(key = "AZURE_KEYVAULT_SP_SCOPE", value = "SYSTEM")
36+
void testValuesSetWithScope(JenkinsRule j) {
37+
AzureKeyVaultGlobalConfiguration configuration = AzureKeyVaultGlobalConfiguration.get();
38+
39+
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
40+
assertThat(configuration.getKeyVaultURL(), is("https://mine.vault.azure.net"));
41+
42+
Credentials credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
43+
44+
assertThat(credentials, instanceOf(AzureImdsCredentials.class));
45+
assertThat(credentials.getScope(), is(CredentialsScope.SYSTEM));
3046
}
3147
}

src/test/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfigurationSystemPropertySPSecretFileTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jenkinsci.plugins.azurekeyvaultplugin;
22

33
import com.cloudbees.plugins.credentials.Credentials;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
45
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
56
import com.microsoft.azure.util.AzureCredentials;
67
import java.util.List;
@@ -34,6 +35,7 @@ void after() {
3435
System.clearProperty("jenkins.azure-keyvault.sp.subscription_id");
3536
System.clearProperty("jenkins.azure-keyvault.sp.tenant_id");
3637
System.clearProperty("jenkins.azure-keyvault.uami.enabled");
38+
System.clearProperty("jenkins.azure-keyvault.sp.scope");
3739
}
3840

3941
@Test
@@ -52,13 +54,15 @@ void testValuesSet(JenkinsRule j) {
5254
assertThat(azureCredentials.getPlainClientSecret(), is("1255534"));
5355
assertThat(azureCredentials.getSubscriptionId(), is("5678"));
5456
assertThat(azureCredentials.getTenant(), is("tenant_id"));
57+
assertThat(azureCredentials.getScope(), is(CredentialsScope.GLOBAL));
5558

5659
// Test updating value
5760
System.setProperty("jenkins.azure-keyvault.url", "https://mine2.vault.azure.net");
5861
System.setProperty("jenkins.azure-keyvault.sp.client_id", "5678");
5962
System.setProperty("jenkins.azure-keyvault.sp.client_secret_file", "src/test/resources/org/jenkinsci/plugins/azurekeyvaultplugin/secretfile2");
6063
System.setProperty("jenkins.azure-keyvault.sp.subscription_id", "9999");
6164
System.setProperty("jenkins.azure-keyvault.sp.tenant_id", "11111");
65+
System.setProperty("jenkins.azure-keyvault.sp.scope", "system");
6266

6367
configuration = AzureKeyVaultGlobalConfiguration.get();
6468

@@ -74,5 +78,6 @@ void testValuesSet(JenkinsRule j) {
7478
assertThat(azureCredentialsUpdated.getPlainClientSecret(), is("99999"));
7579
assertThat(azureCredentialsUpdated.getSubscriptionId(), is("9999"));
7680
assertThat(azureCredentialsUpdated.getTenant(), is("11111"));
81+
assertThat(azureCredentialsUpdated.getScope(), is(CredentialsScope.SYSTEM));
7782
}
7883
}

src/test/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfigurationSystemPropertySPTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jenkinsci.plugins.azurekeyvaultplugin;
22

33
import com.cloudbees.plugins.credentials.Credentials;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
45
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
56
import com.microsoft.azure.util.AzureCredentials;
67
import com.microsoft.azure.util.AzureImdsCredentials;
@@ -35,6 +36,7 @@ void after() {
3536
System.clearProperty("jenkins.azure-keyvault.sp.subscription_id");
3637
System.clearProperty("jenkins.azure-keyvault.sp.tenant_id");
3738
System.clearProperty("jenkins.azure-keyvault.uami.enabled");
39+
System.clearProperty("jenkins.azure-keyvault.sp.scope");
3840
}
3941

4042
@Test
@@ -53,13 +55,15 @@ void testValuesSet(JenkinsRule j) {
5355
assertThat(azureCredentials.getPlainClientSecret(), is("1255534"));
5456
assertThat(azureCredentials.getSubscriptionId(), is("5678"));
5557
assertThat(azureCredentials.getTenant(), is("tenant_id"));
58+
assertThat(azureCredentials.getScope(), is(CredentialsScope.GLOBAL));
5659

5760
// Test updating value
5861
System.setProperty("jenkins.azure-keyvault.url", "https://mine2.vault.azure.net");
5962
System.setProperty("jenkins.azure-keyvault.sp.client_id", "5678");
6063
System.setProperty("jenkins.azure-keyvault.sp.client_secret", "99999");
6164
System.setProperty("jenkins.azure-keyvault.sp.subscription_id", "9999");
6265
System.setProperty("jenkins.azure-keyvault.sp.tenant_id", "11111");
66+
System.setProperty("jenkins.azure-keyvault.sp.scope", "system");
6367

6468
configuration = AzureKeyVaultGlobalConfiguration.get();
6569

@@ -75,6 +79,7 @@ void testValuesSet(JenkinsRule j) {
7579
assertThat(azureCredentialsUpdated.getPlainClientSecret(), is("99999"));
7680
assertThat(azureCredentialsUpdated.getSubscriptionId(), is("9999"));
7781
assertThat(azureCredentialsUpdated.getTenant(), is("11111"));
82+
assertThat(azureCredentialsUpdated.getScope(), is(CredentialsScope.SYSTEM));
7883
}
7984

8085
@Test
@@ -93,12 +98,15 @@ void testChangingFromSptoUami(JenkinsRule j) {
9398
assertThat(azureCredentials.getPlainClientSecret(), is("1255534"));
9499
assertThat(azureCredentials.getSubscriptionId(), is("5678"));
95100
assertThat(azureCredentials.getTenant(), is("tenant_id"));
101+
assertThat(azureCredentials.getScope(), is(CredentialsScope.GLOBAL));
96102

97103
System.setProperty("jenkins.azure-keyvault.uami.enabled", "true");
104+
System.setProperty("jenkins.azure-keyvault.sp.scope", "system");
98105

99106
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
100107

101108
credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
102109
assertThat(credentials, instanceOf(AzureImdsCredentials.class));
110+
assertThat(credentials.getScope(), is(CredentialsScope.SYSTEM));
103111
}
104112
}

src/test/java/org/jenkinsci/plugins/azurekeyvaultplugin/AzureKeyVaultGlobalConfigurationSystemPropertyUamiTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.jenkinsci.plugins.azurekeyvaultplugin;
22

33
import com.cloudbees.plugins.credentials.Credentials;
4+
import com.cloudbees.plugins.credentials.CredentialsScope;
45
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
56
import com.microsoft.azure.util.AzureImdsCredentials;
67
import org.junit.jupiter.api.AfterEach;
@@ -26,6 +27,7 @@ void before() {
2627
void after() {
2728
System.clearProperty("jenkins.azure-keyvault.url");
2829
System.clearProperty("jenkins.azure-keyvault.uami.enabled");
30+
System.clearProperty("jenkins.azure-keyvault.sp.scope");
2931
}
3032

3133
@Test
@@ -38,5 +40,21 @@ void testValuesSet(JenkinsRule j) {
3840
Credentials credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
3941

4042
assertThat(credentials, instanceOf(AzureImdsCredentials.class));
43+
assertThat(credentials.getScope(), is(CredentialsScope.GLOBAL));
44+
45+
}
46+
@Test
47+
void testValuesSetWithScope(JenkinsRule j) {
48+
System.setProperty("jenkins.azure-keyvault.sp.scope", "system");
49+
50+
AzureKeyVaultGlobalConfiguration configuration = AzureKeyVaultGlobalConfiguration.get();
51+
52+
assertThat(configuration.getCredentialID(), is(AzureKeyVaultGlobalConfiguration.GENERATED_ID));
53+
assertThat(configuration.getKeyVaultURL(), is("https://mine.vault.azure.net"));
54+
55+
Credentials credentials = SystemCredentialsProvider.getInstance().getCredentials().get(0);
56+
57+
assertThat(credentials, instanceOf(AzureImdsCredentials.class));
58+
assertThat(credentials.getScope(), is(CredentialsScope.SYSTEM));
4159
}
4260
}

0 commit comments

Comments
 (0)