Skip to content
This repository has been archived by the owner on Nov 4, 2024. It is now read-only.

Commit

Permalink
feat: Add description, lastUpdatedTime, and lastUpdatedBy to credentials
Browse files Browse the repository at this point in the history
Signed-off-by: Eamonn Mansour <[email protected]>
  • Loading branch information
eamansour committed Oct 29, 2024
1 parent 8947ffb commit 1528805
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 18 deletions.
2 changes: 1 addition & 1 deletion .secrets.baseline
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"hashed_secret": "1beb7496ebbe82c61151be093956d83dac625c13",
"is_secret": false,
"is_verified": false,
"line_number": 396,
"line_number": 430,
"type": "Secret Keyword",
"verified_result": null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.net.URI;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import java.util.Properties;
import java.util.Set;
import java.util.HashMap;
Expand Down Expand Up @@ -44,7 +45,7 @@ public class Etcd3CredentialsStore extends Etcd3Store implements ICredentialsSto
private final IEncryptionService encryptionService;

private static final String CREDS_NAMESPACE = "secure";
private static final String CREDS_PROPERTY_PREFIX = CREDS_NAMESPACE + ".credentials";
private static final String CREDS_PROPERTY_PREFIX = CREDS_NAMESPACE + ".credentials.";

/**
* This constructor instantiates the Key value client that can retrieve values
Expand Down Expand Up @@ -89,11 +90,8 @@ public Etcd3CredentialsStore(SecretKeySpec key, IEncryptionService encryptionSer
public ICredentials getCredentials(String credentialsId) throws CredentialsException {
ICredentials credentials = null;
try {
String token = get(CREDS_PROPERTY_PREFIX + "." + credentialsId + ".token");
String username = get(CREDS_PROPERTY_PREFIX + "." + credentialsId + ".username");
String password = get(CREDS_PROPERTY_PREFIX + "." + credentialsId + ".password");

credentials = convertValuesIntoCredentials(username, password, token);
Map<String, String> credentialsProperties = getPrefix(CREDS_PROPERTY_PREFIX + credentialsId);
credentials = convertPropertiesIntoCredentials(credentialsProperties, credentialsId);

} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
Expand All @@ -118,14 +116,14 @@ public void shutdown() throws CredentialsException {
@Override
public void setCredentials(String credentialsId, ICredentials credentials) throws CredentialsException {
Properties credentialProperties = credentials.toProperties(credentialsId);
Properties metadataProperties = credentials.getMetadataProperties(credentialsId);

try {
// Clear any existing properties with the same credentials ID
deleteCredentials(credentialsId);

for (Entry<Object, Object> property : credentialProperties.entrySet()) {
put((String) property.getKey(), encryptionService.encrypt((String) property.getValue()));
}
putAllProperties(credentialProperties, true);
putAllProperties(metadataProperties, false);
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
throw new CredentialsException("Failed to set credentials", e);
Expand All @@ -135,7 +133,7 @@ public void setCredentials(String credentialsId, ICredentials credentials) throw
@Override
public void deleteCredentials(String credentialsId) throws CredentialsException {
try {
deletePrefix(CREDS_PROPERTY_PREFIX + "." + credentialsId);
deletePrefix(CREDS_PROPERTY_PREFIX + credentialsId);
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
throw new CredentialsException("Failed to delete credentials", e);
Expand All @@ -146,7 +144,7 @@ public void deleteCredentials(String credentialsId) throws CredentialsException
public Map<String, ICredentials> getAllCredentials() throws CredentialsException {
Map<String, ICredentials> credentials = new HashMap<>();
try {
Map<String, String> credentialsKeyValues = getPrefix(CREDS_PROPERTY_PREFIX + ".");
Map<String, String> credentialsKeyValues = getPrefix(CREDS_PROPERTY_PREFIX);

// Build a set of all credential IDs stored in etcd
Set<Entry<String, String>> credentialsEntries = credentialsKeyValues.entrySet();
Expand All @@ -164,11 +162,7 @@ public Map<String, ICredentials> getAllCredentials() throws CredentialsException
.filter(entry -> entry.getKey().contains("." + id + "."))
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));

String username = idProperties.get(CREDS_PROPERTY_PREFIX + "." + id + ".username");
String password = idProperties.get(CREDS_PROPERTY_PREFIX + "." + id + ".password");
String token = idProperties.get(CREDS_PROPERTY_PREFIX + "." + id + ".token");

ICredentials convertedCredentials = convertValuesIntoCredentials(username, password, token);
ICredentials convertedCredentials = convertPropertiesIntoCredentials(idProperties, id);
if (convertedCredentials != null) {
credentials.put(id, convertedCredentials);
}
Expand All @@ -181,7 +175,11 @@ public Map<String, ICredentials> getAllCredentials() throws CredentialsException
return credentials;
}

private ICredentials convertValuesIntoCredentials(String username, String password, String token) throws CredentialsException {
private ICredentials convertPropertiesIntoCredentials(Map<String, String> credProperties, String credentialsId) throws CredentialsException {
String token = credProperties.get(CREDS_PROPERTY_PREFIX + credentialsId + ".token");
String username = credProperties.get(CREDS_PROPERTY_PREFIX + credentialsId + ".username");
String password = credProperties.get(CREDS_PROPERTY_PREFIX + credentialsId + ".password");

ICredentials credentials = null;

// Check if the credentials are UsernameToken or Token
Expand All @@ -197,6 +195,18 @@ private ICredentials convertValuesIntoCredentials(String username, String passwo
credentials = new CredentialsUsername(key, username);
}
}

if (credentials != null) {
String description = credProperties.get(CREDS_PROPERTY_PREFIX + credentialsId + ".description");
String lastUpdatedTime = credProperties.get(CREDS_PROPERTY_PREFIX + credentialsId + ".lastUpdated.time");
String lastUpdatedUser = credProperties.get(CREDS_PROPERTY_PREFIX + credentialsId + ".lastUpdated.user");

credentials.setDescription(description);
credentials.setLastUpdatedByUser(lastUpdatedUser);
if (lastUpdatedTime != null) {
credentials.setLastUpdatedTime(Instant.parse(lastUpdatedTime));
}
}
return credentials;
}

Expand All @@ -211,4 +221,15 @@ private String getCredentialsIdFromKey(String key) {
}
return credentialsId;
}

private void putAllProperties(Properties properties, boolean encryptValues) throws CredentialsException, InterruptedException, ExecutionException {
for (Entry<Object, Object> property : properties.entrySet()) {
String key = (String) property.getKey();
String value = (String) property.getValue();
if (encryptValues) {
value = encryptionService.encrypt(value);
}
put(key, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
package dev.galasa.etcd.internal;

import java.time.Instant;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -217,6 +218,39 @@ public void testGetUsernamePasswordCredentialsReturnsCredentialsOk() throws Exce
assertThat(creds.getPassword()).isEqualTo(password);
}

@Test
public void testGetUsernamePasswordCredentialsWithMetadataReturnsCredentialsOk() throws Exception {
// Given...
MockEncryptionService mockEncryptionService = new MockEncryptionService();
String credsId = "CRED1";
String username = "my-user";
String password = "not-a-password";
String description = "a description of my credentials";
String lastUpdatedUser = "myUsername";
Instant lastUpdatedTime = Instant.EPOCH;

Map<String, String> mockCreds = new HashMap<>();
mockCreds.put("secure.credentials." + credsId + ".username", username);
mockCreds.put("secure.credentials." + credsId + ".password", password);
mockCreds.put("secure.credentials." + credsId + ".description", description);
mockCreds.put("secure.credentials." + credsId + ".lastUpdated.time", lastUpdatedTime.toString());
mockCreds.put("secure.credentials." + credsId + ".lastUpdated.user", lastUpdatedUser);

MockEtcdClient mockClient = new MockEtcdClient(mockCreds);
Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient);

// When...
CredentialsUsernamePassword creds = (CredentialsUsernamePassword) store.getCredentials(credsId);

// Then...
assertThat(creds).isNotNull();
assertThat(creds.getUsername()).isEqualTo(username);
assertThat(creds.getPassword()).isEqualTo(password);
assertThat(creds.getDescription()).isEqualTo(description);
assertThat(creds.getLastUpdatedByUser()).isEqualTo(lastUpdatedUser);
assertThat(creds.getLastUpdatedTime()).isEqualTo(lastUpdatedTime);
}

@Test
public void testGetUsernameTokenCredentialsReturnsCredentialsOk() throws Exception {
// Given...
Expand Down Expand Up @@ -425,4 +459,39 @@ public void testShutdownClosesEtcdClientsOk() throws Exception {
// Then...
assertThat(mockClient.isClientShutDown()).isTrue();
}

@Test
public void testSetCredentialsWithMetadataSetsCredentialsOk() throws Exception {
// Given...
MockEncryptionService mockEncryptionService = new MockEncryptionService();
String credsId = "CRED1";
String username = "a-username";
String lastUpdatedUser = "myuser";
Instant lastUpdatedTime = Instant.EPOCH;
String description = "this is a description of my username secret";


Map<String, String> mockCreds = new HashMap<>();
MockEtcdClient mockClient = new MockEtcdClient(mockCreds);
Etcd3CredentialsStore store = new Etcd3CredentialsStore(null, mockEncryptionService, mockClient);

CredentialsUsername mockUsernameCreds = new CredentialsUsername(username);
mockUsernameCreds.setDescription(description);
mockUsernameCreds.setLastUpdatedByUser(lastUpdatedUser);
mockUsernameCreds.setLastUpdatedTime(lastUpdatedTime);

// When...
store.setCredentials(credsId, mockUsernameCreds);

// Then...
assertThat(mockCreds).hasSize(4);
assertThat(mockCreds.get("secure.credentials." + credsId + ".username")).isEqualTo(username);
assertThat(mockCreds.get("secure.credentials." + credsId + ".description")).isEqualTo(description);
assertThat(mockCreds.get("secure.credentials." + credsId + ".lastUpdated.time")).isEqualTo(lastUpdatedTime.toString());
assertThat(mockCreds.get("secure.credentials." + credsId + ".lastUpdated.user")).isEqualTo(lastUpdatedUser);

// The credentials should have been encrypted when being set, but the metadata should not be encrypted
assertThat(mockEncryptionService.getEncryptCount()).isEqualTo(1);
assertThat(mockEncryptionService.getDecryptCount()).isEqualTo(0);
}
}

0 comments on commit 1528805

Please sign in to comment.