mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUACAMOLE-641: Alternatively download .pem files for private keys.
This commit is contained in:
@@ -24,13 +24,10 @@ import com.google.inject.Singleton;
|
|||||||
import com.keepersecurity.secretsManager.core.Hosts;
|
import com.keepersecurity.secretsManager.core.Hosts;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperFile;
|
import com.keepersecurity.secretsManager.core.KeeperFile;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecord;
|
import com.keepersecurity.secretsManager.core.KeeperRecord;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecordData;
|
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecordField;
|
|
||||||
import com.keepersecurity.secretsManager.core.KeeperSecrets;
|
import com.keepersecurity.secretsManager.core.KeeperSecrets;
|
||||||
import com.keepersecurity.secretsManager.core.Login;
|
import com.keepersecurity.secretsManager.core.Login;
|
||||||
import com.keepersecurity.secretsManager.core.Notation;
|
import com.keepersecurity.secretsManager.core.Notation;
|
||||||
import com.keepersecurity.secretsManager.core.SecretsManager;
|
import com.keepersecurity.secretsManager.core.SecretsManager;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -446,16 +443,10 @@ public class KsmClient {
|
|||||||
cacheLock.readLock().lock();
|
cacheLock.readLock().lock();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
// Retrieve any relevant file asynchronously
|
||||||
Matcher fileNotationMatcher = KEEPER_FILE_NOTATION.matcher(notation);
|
Matcher fileNotationMatcher = KEEPER_FILE_NOTATION.matcher(notation);
|
||||||
if (fileNotationMatcher.matches()) {
|
if (fileNotationMatcher.matches())
|
||||||
|
return recordService.download(Notation.getFile(cachedSecrets, notation));
|
||||||
// Retrieve any relevant file asynchronously
|
|
||||||
KeeperFile file = Notation.getFile(cachedSecrets, notation);
|
|
||||||
return CompletableFuture.supplyAsync(() -> {
|
|
||||||
return new String(SecretsManager.downloadFile(file), StandardCharsets.UTF_8);
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve string values synchronously
|
// Retrieve string values synchronously
|
||||||
return CompletableFuture.completedFuture(Notation.getValue(cachedSecrets, notation));
|
return CompletableFuture.completedFuture(Notation.getValue(cachedSecrets, notation));
|
||||||
|
@@ -23,6 +23,7 @@ import com.google.inject.Singleton;
|
|||||||
import com.keepersecurity.secretsManager.core.HiddenField;
|
import com.keepersecurity.secretsManager.core.HiddenField;
|
||||||
import com.keepersecurity.secretsManager.core.Host;
|
import com.keepersecurity.secretsManager.core.Host;
|
||||||
import com.keepersecurity.secretsManager.core.Hosts;
|
import com.keepersecurity.secretsManager.core.Hosts;
|
||||||
|
import com.keepersecurity.secretsManager.core.KeeperFile;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecord;
|
import com.keepersecurity.secretsManager.core.KeeperRecord;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecordData;
|
import com.keepersecurity.secretsManager.core.KeeperRecordData;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecordField;
|
import com.keepersecurity.secretsManager.core.KeeperRecordField;
|
||||||
@@ -30,8 +31,12 @@ import com.keepersecurity.secretsManager.core.KeyPair;
|
|||||||
import com.keepersecurity.secretsManager.core.KeyPairs;
|
import com.keepersecurity.secretsManager.core.KeyPairs;
|
||||||
import com.keepersecurity.secretsManager.core.Login;
|
import com.keepersecurity.secretsManager.core.Login;
|
||||||
import com.keepersecurity.secretsManager.core.Password;
|
import com.keepersecurity.secretsManager.core.Password;
|
||||||
|
import com.keepersecurity.secretsManager.core.SecretsManager;
|
||||||
import com.keepersecurity.secretsManager.core.Text;
|
import com.keepersecurity.secretsManager.core.Text;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -77,6 +82,13 @@ public class KsmRecordService {
|
|||||||
private static final Pattern PRIVATE_KEY_LABEL_PATTERN =
|
private static final Pattern PRIVATE_KEY_LABEL_PATTERN =
|
||||||
Pattern.compile("private\\s*key", Pattern.CASE_INSENSITIVE);
|
Pattern.compile("private\\s*key", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regular expression which matches the filenames of private keys attached
|
||||||
|
* to Keeper records.
|
||||||
|
*/
|
||||||
|
private static final Pattern PRIVATE_KEY_FILENAME_PATTERN =
|
||||||
|
Pattern.compile(".*\\.pem", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the single value stored in the given list. If the list is empty
|
* Returns the single value stored in the given list. If the list is empty
|
||||||
* or contains multiple values, null is returned.
|
* or contains multiple values, null is returned.
|
||||||
@@ -234,6 +246,70 @@ public class KsmRecordService {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file attached to the give Keeper record whose filename
|
||||||
|
* matches the given pattern. If there are no such files, or multiple such
|
||||||
|
* files, null is returned.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The record to retrieve the file from.
|
||||||
|
*
|
||||||
|
* @param filenamePattern
|
||||||
|
* The pattern to match filenames against.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The single matching file attached to the given Keeper record, or
|
||||||
|
* null if there is not exactly one matching file.
|
||||||
|
*/
|
||||||
|
private KeeperFile getFile(KeeperRecord record, Pattern filenamePattern) {
|
||||||
|
|
||||||
|
List<KeeperFile> files = record.getFiles();
|
||||||
|
if (files == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
KeeperFile foundFile = null;
|
||||||
|
for (KeeperFile file : files) {
|
||||||
|
|
||||||
|
// Ignore files whose filenames do not match
|
||||||
|
Matcher filenameMatcher = filenamePattern.matcher(file.getData().getName());
|
||||||
|
if (!filenameMatcher.matches())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Ignore ambiguous fields
|
||||||
|
if (foundFile != null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
foundFile = file;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundFile;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Downloads the given file from the Keeper vault asynchronously. All files
|
||||||
|
* are read as UTF-8.
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* The file to download, which may be null.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A Future which resolves with the contents of the file once
|
||||||
|
* downloaded. If no file was provided (file was null), this Future
|
||||||
|
* resolves with null.
|
||||||
|
*/
|
||||||
|
public Future<String> download(final KeeperFile file) {
|
||||||
|
|
||||||
|
if (file == null)
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
return new String(SecretsManager.downloadFile(file), StandardCharsets.UTF_8);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the single hostname (or address) associated with the given
|
* Returns the single hostname (or address) associated with the given
|
||||||
* record. If the record has no associated hostname, or multiple hostnames,
|
* record. If the record has no associated hostname, or multiple hostnames,
|
||||||
@@ -341,23 +417,30 @@ public class KsmRecordService {
|
|||||||
* Returns the private key associated with the given record. If the record
|
* Returns the private key associated with the given record. If the record
|
||||||
* has no associated private key, or multiple private keys, null is
|
* has no associated private key, or multiple private keys, null is
|
||||||
* returned. Private keys are retrieved from "KeyPairs" fields.
|
* returned. Private keys are retrieved from "KeyPairs" fields.
|
||||||
* Alternatively, private keys are retrieved from custom fields with the
|
* Alternatively, private keys are retrieved from PEM-type attachments or
|
||||||
* label "private key" (case-insensitive, space optional) if they are
|
* custom fields with the label "private key" (case-insensitive, space
|
||||||
* "KeyPairs", "Password", or "Hidden" fields.
|
* optional) if they are "KeyPairs", "Password", or "Hidden" fields. If
|
||||||
|
* file downloads are required, they will be performed asynchronously.
|
||||||
*
|
*
|
||||||
* @param record
|
* @param record
|
||||||
* The record to retrieve the private key from.
|
* The record to retrieve the private key from.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* The private key associated with the given record, or null if the
|
* A Future which resolves with the private key associated with the
|
||||||
* record has no associated private key or multiple private keys.
|
* given record. If the record has no associated private key or
|
||||||
|
* multiple private keys, the returned Future will resolve to null.
|
||||||
*/
|
*/
|
||||||
public String getPrivateKey(KeeperRecord record) {
|
public Future<String> getPrivateKey(KeeperRecord record) {
|
||||||
|
|
||||||
// Attempt to find single matching keypair field
|
// Attempt to find single matching keypair field
|
||||||
KeyPairs keyPairsField = getField(record, KeyPairs.class, PRIVATE_KEY_LABEL_PATTERN);
|
KeyPairs keyPairsField = getField(record, KeyPairs.class, PRIVATE_KEY_LABEL_PATTERN);
|
||||||
if (keyPairsField != null)
|
if (keyPairsField != null)
|
||||||
return getSingleValue(keyPairsField.getValue(), KeyPair::getPrivateKey);
|
return CompletableFuture.completedFuture(getSingleValue(keyPairsField.getValue(), KeyPair::getPrivateKey));
|
||||||
|
|
||||||
|
// Lacking a typed keypair field, prefer a PEM-type attachment
|
||||||
|
KeeperFile keyFile = getFile(record, PRIVATE_KEY_FILENAME_PATTERN);
|
||||||
|
if (keyFile != null)
|
||||||
|
return download(keyFile);
|
||||||
|
|
||||||
KeeperRecordData data = record.getData();
|
KeeperRecordData data = record.getData();
|
||||||
List<KeeperRecordField> custom = data.getCustom();
|
List<KeeperRecordField> custom = data.getCustom();
|
||||||
@@ -365,14 +448,14 @@ public class KsmRecordService {
|
|||||||
// Use password "private key" custom field as fallback ...
|
// Use password "private key" custom field as fallback ...
|
||||||
Password passwordField = getField(custom, Password.class, PRIVATE_KEY_LABEL_PATTERN);
|
Password passwordField = getField(custom, Password.class, PRIVATE_KEY_LABEL_PATTERN);
|
||||||
if (passwordField != null)
|
if (passwordField != null)
|
||||||
return getSingleValue(passwordField.getValue());
|
return CompletableFuture.completedFuture(getSingleValue(passwordField.getValue()));
|
||||||
|
|
||||||
// ... or hidden "private key" custom field
|
// ... or hidden "private key" custom field
|
||||||
HiddenField hiddenField = getField(custom, HiddenField.class, PRIVATE_KEY_LABEL_PATTERN);
|
HiddenField hiddenField = getField(custom, HiddenField.class, PRIVATE_KEY_LABEL_PATTERN);
|
||||||
if (hiddenField != null)
|
if (hiddenField != null)
|
||||||
return getSingleValue(hiddenField.getValue());
|
return CompletableFuture.completedFuture(getSingleValue(hiddenField.getValue()));
|
||||||
|
|
||||||
return null;
|
return CompletableFuture.completedFuture(null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -109,9 +109,8 @@ public class KsmSecretService implements VaultSecretService {
|
|||||||
tokens.put(prefix + "PASSPHRASE", CompletableFuture.completedFuture(passphrase));
|
tokens.put(prefix + "PASSPHRASE", CompletableFuture.completedFuture(passphrase));
|
||||||
|
|
||||||
// Private key of server-related record
|
// Private key of server-related record
|
||||||
String privateKey = recordService.getPrivateKey(record);
|
Future<String> privateKey = recordService.getPrivateKey(record);
|
||||||
if (privateKey != null)
|
tokens.put(prefix + "KEY", privateKey);
|
||||||
tokens.put(prefix + "KEY", CompletableFuture.completedFuture(privateKey));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user