Skip to content

Commit 6f511dc

Browse files
committed
JSON-format username + password
1 parent 9666c6a commit 6f511dc

File tree

4 files changed

+183
-0
lines changed

4 files changed

+183
-0
lines changed

src/main/java/io/jenkins/plugins/credentials/secretsmanager/factory/CredentialsFactory.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.jenkins.plugins.credentials.secretsmanager.factory.file.AwsFileCredentials;
1515
import io.jenkins.plugins.credentials.secretsmanager.factory.ssh_user_private_key.AwsSshUserPrivateKey;
1616
import io.jenkins.plugins.credentials.secretsmanager.factory.string.AwsStringCredentials;
17+
import io.jenkins.plugins.credentials.secretsmanager.factory.username_password.AwsJsonUsernamePasswordCredentials;
1718
import io.jenkins.plugins.credentials.secretsmanager.factory.username_password.AwsUsernamePasswordCredentials;
1819

1920
import java.util.Map;
@@ -46,6 +47,8 @@ public static Optional<StandardCredentials> create(String arn, String name, Stri
4647
return Optional.of(new AwsStringCredentials(name, description, new SecretSupplier(client, arn)));
4748
case Type.usernamePassword:
4849
return Optional.of(new AwsUsernamePasswordCredentials(name, description, new SecretSupplier(client, arn), username));
50+
case Type.jsonUsernamePassword:
51+
return Optional.of(new AwsJsonUsernamePasswordCredentials(name, description, new SecretSupplier(client, arn)));
4952
case Type.sshUserPrivateKey:
5053
return Optional.of(new AwsSshUserPrivateKey(name, description, new StringSupplier(client, arn), username));
5154
case Type.certificate:

src/main/java/io/jenkins/plugins/credentials/secretsmanager/factory/Type.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public abstract class Type {
77
public static final String certificate = "certificate";
88
public static final String file = "file";
99
public static final String usernamePassword = "usernamePassword";
10+
public static final String jsonUsernamePassword = "jsonUsernamePassword";
1011
public static final String sshUserPrivateKey = "sshUserPrivateKey";
1112
public static final String string = "string";
1213

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package io.jenkins.plugins.credentials.secretsmanager.factory.username_password;
2+
3+
import java.util.function.Supplier;
4+
5+
import javax.annotation.Nonnull;
6+
7+
import org.kohsuke.accmod.Restricted;
8+
import org.kohsuke.accmod.restrictions.NoExternalUse;
9+
10+
import com.cloudbees.plugins.credentials.CredentialsProvider;
11+
import com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials;
12+
13+
import edu.umd.cs.findbugs.annotations.NonNull;
14+
import hudson.Extension;
15+
import hudson.util.Secret;
16+
import io.jenkins.plugins.credentials.secretsmanager.AwsCredentialsProvider;
17+
import io.jenkins.plugins.credentials.secretsmanager.Messages;
18+
import io.jenkins.plugins.credentials.secretsmanager.factory.BaseAwsJsonCredentials;
19+
20+
/**
21+
* Similar to {@link AwsUsernamePasswordCredentials} but expects the AWS secret
22+
* data to be JSON containing two fields, {@value #JSON_FIELDNAME_FOR_USERNAME}
23+
* and {@value #JSON_FIELDNAME_FOR_PASSWORD} that provide both the username and
24+
* password. The secret JSON may contain other fields too, but we'll ignore
25+
* them.
26+
*/
27+
public class AwsJsonUsernamePasswordCredentials extends BaseAwsJsonCredentials
28+
implements StandardUsernamePasswordCredentials {
29+
/**
30+
* Name of the JSON field that we expect to be present and to contain the
31+
* credential's username.
32+
*/
33+
@Restricted(NoExternalUse.class)
34+
public static final String JSON_FIELDNAME_FOR_USERNAME = "username";
35+
/**
36+
* Name of the JSON field that we expect to be present and to contain the
37+
* credential's password.
38+
*/
39+
@Restricted(NoExternalUse.class)
40+
public static final String JSON_FIELDNAME_FOR_PASSWORD = "password";
41+
42+
public AwsJsonUsernamePasswordCredentials(String id, String description, Supplier<Secret> usernameAndPasswordJson) {
43+
super(id, description, usernameAndPasswordJson);
44+
}
45+
46+
@NonNull
47+
@Override
48+
public Secret getPassword() {
49+
return Secret.fromString(getFieldFromSecretJson(JSON_FIELDNAME_FOR_PASSWORD));
50+
}
51+
52+
@NonNull
53+
@Override
54+
public String getUsername() {
55+
return getFieldFromSecretJson(JSON_FIELDNAME_FOR_USERNAME);
56+
}
57+
58+
@Extension
59+
@SuppressWarnings("unused")
60+
public static class DescriptorImpl extends BaseStandardCredentialsDescriptor {
61+
@Override
62+
@Nonnull
63+
public String getDisplayName() {
64+
return Messages.usernamePassword();
65+
}
66+
67+
@Override
68+
public String getIconClassName() {
69+
return "icon-credentials-userpass";
70+
}
71+
72+
@Override
73+
public boolean isApplicable(CredentialsProvider provider) {
74+
return provider instanceof AwsCredentialsProvider;
75+
}
76+
}
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package io.jenkins.plugins.credentials.secretsmanager.factory.username_password;
2+
3+
import static io.jenkins.plugins.credentials.secretsmanager.factory.BaseAwsJsonCredentialsTest.mkJson;
4+
import static io.jenkins.plugins.credentials.secretsmanager.factory.BaseAwsJsonCredentialsTest.assertThatJsonCredentialsDescriptorIsTheSameAsTheDescriptorForNonJsonCredentials;
5+
import static org.assertj.core.api.Assertions.assertThat;
6+
7+
import java.util.function.Supplier;
8+
9+
import org.junit.Test;
10+
11+
import com.cloudbees.plugins.credentials.CredentialsUnavailableException;
12+
13+
import hudson.util.Secret;
14+
import io.jenkins.plugins.credentials.secretsmanager.Messages;
15+
import io.jenkins.plugins.credentials.secretsmanager.factory.BaseAwsJsonCredentialsTest.StubSupplier;
16+
17+
public class AwsJsonUsernamePasswordCredentialsTest {
18+
@Test
19+
public void getPasswordGivenValidJsonThenReturnsSecretPassword() {
20+
// Given
21+
final String id = "testId";
22+
final String description = "some test description";
23+
final String expected = "mySecretPassword";
24+
final String usernamePasswordJson = mkUsernameAndPasswordJson("myUsername", expected);
25+
final Secret stubSecret = Secret.fromString(usernamePasswordJson);
26+
final Supplier<Secret> stubSupplier = new StubSupplier<>(stubSecret);
27+
final AwsJsonUsernamePasswordCredentials instance = new AwsJsonUsernamePasswordCredentials(id, description,
28+
stubSupplier);
29+
30+
// When
31+
final Secret actualSecret = instance.getPassword();
32+
final String actualPassword = actualSecret.getPlainText();
33+
34+
// Then
35+
assertThat(actualPassword).isEqualTo(expected);
36+
}
37+
38+
@Test
39+
public void getUsernameGivenValidJsonThenReturnsUsername() {
40+
// Given
41+
final String id = "testId";
42+
final String description = "some test description";
43+
final String expected = "myUsername";
44+
final String usernamePasswordJson = mkUsernameAndPasswordJson(expected, "mySecretPassword");
45+
final Secret stubSecret = Secret.fromString(usernamePasswordJson);
46+
final Supplier<Secret> stubSupplier = new StubSupplier<>(stubSecret);
47+
final AwsJsonUsernamePasswordCredentials instance = new AwsJsonUsernamePasswordCredentials(id, description,
48+
stubSupplier);
49+
50+
// When
51+
final String actual = instance.getUsername();
52+
53+
// Then
54+
assertThat(actual).isEqualTo(expected);
55+
}
56+
57+
@Test
58+
public void getUsernameGivenInvalidJsonThenReturnsThrows() {
59+
// Given
60+
final String id = "testId";
61+
final String description = "some test description";
62+
final String unexpectedFieldName = "potentiallySecretFieldName";
63+
final String unexpectedValue = "potentiallySecretValue";
64+
final String usernamePasswordJson = mkJson(unexpectedFieldName, unexpectedValue, unexpectedFieldName + "2",
65+
unexpectedValue);
66+
final Secret stubSecret = Secret.fromString(usernamePasswordJson);
67+
final Supplier<Secret> stubSupplier = new StubSupplier<>(stubSecret);
68+
final AwsJsonUsernamePasswordCredentials instance = new AwsJsonUsernamePasswordCredentials(id, description,
69+
stubSupplier);
70+
71+
// When
72+
CredentialsUnavailableException actual = null;
73+
try {
74+
instance.getUsername();
75+
} catch (CredentialsUnavailableException ex) {
76+
actual = ex;
77+
}
78+
79+
// Then
80+
assertThat(actual).isNotNull();
81+
assertThat(actual.getProperty()).isEqualTo("secret");
82+
assertThat(actual.getMessage()).contains(id);
83+
assertThat(actual.getMessage()).contains(AwsJsonUsernamePasswordCredentials.JSON_FIELDNAME_FOR_USERNAME);
84+
assertThat(actual.getMessage())
85+
.contains(Messages.wrongJsonError(id, AwsJsonUsernamePasswordCredentials.JSON_FIELDNAME_FOR_USERNAME));
86+
assertThat(actual.getMessage()).doesNotContain(unexpectedFieldName);
87+
assertThat(actual.getMessage()).doesNotContain(unexpectedValue);
88+
}
89+
90+
@Test
91+
public void ourDescriptorIsTheSameAsDescriptorForNonJsonCredentials() {
92+
// Given
93+
final AwsUsernamePasswordCredentials.DescriptorImpl expected = new AwsUsernamePasswordCredentials.DescriptorImpl();
94+
final AwsJsonUsernamePasswordCredentials.DescriptorImpl instance = new AwsJsonUsernamePasswordCredentials.DescriptorImpl();
95+
assertThatJsonCredentialsDescriptorIsTheSameAsTheDescriptorForNonJsonCredentials(instance, expected);
96+
}
97+
98+
private static String mkUsernameAndPasswordJson(String username, String password) {
99+
return mkJson(AwsJsonUsernamePasswordCredentials.JSON_FIELDNAME_FOR_USERNAME, username,
100+
AwsJsonUsernamePasswordCredentials.JSON_FIELDNAME_FOR_PASSWORD, password);
101+
}
102+
}

0 commit comments

Comments
 (0)