GUACAMOLE-2130: Support for Linked Records in KSM.
This commit is contained in:
11 files changed
+142
-14
No files matched your search
@@ -1,2 +0,0 @@
|
|||||||
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:jar:1.7.2
|
|
||||||
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:jar:1.7.2
|
|
||||||
File renamed without changes.
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
Kotlin Serialization (https://github.com/Kotlin/kotlinx.serialization)
|
Kotlin Serialization (https://github.com/Kotlin/kotlinx.serialization)
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
Version: 1.7.2
|
Version: 1.9.0
|
||||||
From: 'JetBrains s.r.o and respective authors and developers'
|
From: 'JetBrains s.r.o and respective authors and developers'
|
||||||
License(s):
|
License(s):
|
||||||
Apache v2.0
|
Apache v2.0
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:jar:1.9.0
|
||||||
|
org.jetbrains.kotlinx:kotlinx-serialization-core-jvm:jar:1.9.0
|
||||||
@@ -1 +0,0 @@
|
|||||||
com.keepersecurity.secrets-manager:core:jar:17.0.0
|
|
||||||
File renamed without changes.
@@ -2,8 +2,8 @@ Keeper Secrets Manager Java SDK
|
|||||||
(https://github.com/Keeper-Security/secrets-manager)
|
(https://github.com/Keeper-Security/secrets-manager)
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
Version: 17.0.0
|
Version: 17.1.1
|
||||||
From: 'Keeper Security' (https://www.keepersecurity.com/)
|
From: 'Keeper Security' (https://www.keepersecurity.com/)
|
||||||
License(s):
|
License(s):
|
||||||
MIT (bundled/ksm-sdk-17.0.0/LICENSE)
|
MIT (bundled/ksm-sdk-17.1.1/LICENSE)
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
com.keepersecurity.secrets-manager:core:jar:17.1.1
|
||||||
@@ -60,7 +60,7 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.keepersecurity.secrets-manager</groupId>
|
<groupId>com.keepersecurity.secrets-manager</groupId>
|
||||||
<artifactId>core</artifactId>
|
<artifactId>core</artifactId>
|
||||||
<version>17.0.0</version>
|
<version>17.1.1</version>
|
||||||
|
|
||||||
<!-- Correct version conflict (different versions across transitive
|
<!-- Correct version conflict (different versions across transitive
|
||||||
dependencies) -->
|
dependencies) -->
|
||||||
|
|||||||
+117
-1
@@ -24,9 +24,11 @@ import com.google.inject.assistedinject.Assisted;
|
|||||||
import com.google.inject.assistedinject.AssistedInject;
|
import com.google.inject.assistedinject.AssistedInject;
|
||||||
import com.keepersecurity.secretsManager.core.Hosts;
|
import com.keepersecurity.secretsManager.core.Hosts;
|
||||||
import com.keepersecurity.secretsManager.core.KeeperRecord;
|
import com.keepersecurity.secretsManager.core.KeeperRecord;
|
||||||
|
import com.keepersecurity.secretsManager.core.KeeperRecordLink;
|
||||||
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.QueryOptions;
|
||||||
import com.keepersecurity.secretsManager.core.SecretsManager;
|
import com.keepersecurity.secretsManager.core.SecretsManager;
|
||||||
import com.keepersecurity.secretsManager.core.SecretsManagerOptions;
|
import com.keepersecurity.secretsManager.core.SecretsManagerOptions;
|
||||||
|
|
||||||
@@ -156,6 +158,22 @@ public class KsmClient {
|
|||||||
*/
|
*/
|
||||||
private final Map<String, KeeperRecord> cachedRecordsByHost = new HashMap<>();
|
private final Map<String, KeeperRecord> cachedRecordsByHost = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All linked admin records retrieved from Keeper Secrets Manager, where each key is a
|
||||||
|
* KeeperRecord and the value is its associated linked admin KeeperRecord. The contents
|
||||||
|
* of this Map are automatically updated if {@link #validateCache()} refreshes the cache.
|
||||||
|
* This Map must not be accessed without {@link #cacheLock} acquired appropriately.
|
||||||
|
*/
|
||||||
|
private final Map<KeeperRecord, KeeperRecord> cachedLinkedAdminRecords = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All linked launch records retrieved from Keeper Secrets Manager, where each key is a
|
||||||
|
* KeeperRecord and the value is its associated linked launch KeeperRecord. The contents
|
||||||
|
* of this Map are automatically updated if {@link #validateCache()} refreshes the cache.
|
||||||
|
* This Map must not be accessed without {@link #cacheLock} acquired appropriately.
|
||||||
|
*/
|
||||||
|
private final Map<KeeperRecord, KeeperRecord> cachedLinkedLaunchRecords = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The set of all hostnames or IP addresses that are associated with
|
* The set of all hostnames or IP addresses that are associated with
|
||||||
* multiple records, and thus cannot uniquely identify a record. The
|
* multiple records, and thus cannot uniquely identify a record. The
|
||||||
@@ -264,9 +282,16 @@ public class KsmClient {
|
|||||||
if (currentTime - cacheTimestamp < cacheInterval)
|
if (currentTime - cacheTimestamp < cacheInterval)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Enable GraphSync
|
||||||
|
QueryOptions queryOptions = new QueryOptions(
|
||||||
|
Collections.emptyList(),
|
||||||
|
Collections.emptyList(),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
// Attempt to pull all records first, allowing that operation to
|
// Attempt to pull all records first, allowing that operation to
|
||||||
// succeed/fail BEFORE we clear out the last cached success
|
// succeed/fail BEFORE we clear out the last cached success
|
||||||
KeeperSecrets secrets = SecretsManager.getSecrets(ksmConfig);
|
KeeperSecrets secrets = SecretsManager.getSecrets2(ksmConfig, queryOptions);
|
||||||
List<KeeperRecord> records = secrets.getRecords();
|
List<KeeperRecord> records = secrets.getRecords();
|
||||||
|
|
||||||
// Store all secrets within cache
|
// Store all secrets within cache
|
||||||
@@ -275,6 +300,10 @@ public class KsmClient {
|
|||||||
// Clear unambiguous cache of all records by UID
|
// Clear unambiguous cache of all records by UID
|
||||||
cachedRecordsByUid.clear();
|
cachedRecordsByUid.clear();
|
||||||
|
|
||||||
|
// Clear cache of all linked records
|
||||||
|
cachedLinkedAdminRecords.clear();
|
||||||
|
cachedLinkedLaunchRecords.clear();
|
||||||
|
|
||||||
// Clear cache of host-based records
|
// Clear cache of host-based records
|
||||||
cachedAmbiguousHosts.clear();
|
cachedAmbiguousHosts.clear();
|
||||||
cachedRecordsByHost.clear();
|
cachedRecordsByHost.clear();
|
||||||
@@ -339,6 +368,9 @@ public class KsmClient {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Run through again and build a cache of linked records
|
||||||
|
records.forEach(record -> cacheLinkedRecords(record));
|
||||||
|
|
||||||
// Cache has been refreshed
|
// Cache has been refreshed
|
||||||
this.cacheTimestamp = System.currentTimeMillis();
|
this.cacheTimestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
@@ -397,6 +429,42 @@ public class KsmClient {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates {@link #cachedLinkedAdminRecords} and {@link #cachedLinkedLaunchRecords}
|
||||||
|
* with any linked admin or launch records from the provided record. The write lock of
|
||||||
|
* {@link #cacheLock} must already be acquired before invoking this function.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The record whose linked records should be processed and cached.
|
||||||
|
*/
|
||||||
|
private void cacheLinkedRecords(KeeperRecord record) {
|
||||||
|
List<KeeperRecordLink> links = record.getLinks();
|
||||||
|
|
||||||
|
if (links != null && !links.isEmpty()) {
|
||||||
|
|
||||||
|
for (KeeperRecordLink link : links) {
|
||||||
|
|
||||||
|
KeeperRecord linkedRecord = cachedRecordsByUid.get(link.getRecordUid());
|
||||||
|
|
||||||
|
if (linkedRecord != null && link.isAdminUser()) {
|
||||||
|
if (cachedLinkedAdminRecords.containsKey(record)) {
|
||||||
|
logger.warn("Multiple 'admin' linked records exist "
|
||||||
|
+ "for record UID: {}", record.getRecordUid());
|
||||||
|
}
|
||||||
|
cachedLinkedAdminRecords.put(record, linkedRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (linkedRecord != null && link.isLaunchCredential()) {
|
||||||
|
if (cachedLinkedLaunchRecords.containsKey(record)) {
|
||||||
|
logger.warn("Multiple 'launch' linked records exist "
|
||||||
|
+ "for record UID: {}", record.getRecordUid());
|
||||||
|
}
|
||||||
|
cachedLinkedLaunchRecords.put(record, linkedRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Associates the given record with the given user, and optional domain.
|
* Associates the given record with the given user, and optional domain.
|
||||||
* The given username or domain may be null. Both {@link #cachedRecordsByUser}
|
* The given username or domain may be null. Both {@link #cachedRecordsByUser}
|
||||||
@@ -587,6 +655,54 @@ public class KsmClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the linked admin record for the given Keeper record, if one exists.
|
||||||
|
* If no such linked admin record exists, null is returned.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The KeeperRecord for which to retrieve the linked admin record.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The linked KeeperRecord, or null if there is no such record.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error occurs that prevents the linked record from being retrieved.
|
||||||
|
*/
|
||||||
|
public KeeperRecord getLinkedAdminRecord(KeeperRecord record) throws GuacamoleException {
|
||||||
|
validateCache();
|
||||||
|
cacheLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return cachedLinkedAdminRecords.get(record);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
cacheLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the linked launch record for the given Keeper record, if one exists.
|
||||||
|
* If no such linked launch record exists, null is returned.
|
||||||
|
*
|
||||||
|
* @param record
|
||||||
|
* The KeeperRecord for which to retrieve the linked launch record.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The linked KeeperRecord, or null if there is no such record.
|
||||||
|
*
|
||||||
|
* @throws GuacamoleException
|
||||||
|
* If an error occurs that prevents the linked record from being retrieved.
|
||||||
|
*/
|
||||||
|
public KeeperRecord getLinkedLaunchRecord(KeeperRecord record) throws GuacamoleException {
|
||||||
|
validateCache();
|
||||||
|
cacheLock.readLock().lock();
|
||||||
|
try {
|
||||||
|
return cachedLinkedLaunchRecords.get(record);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
cacheLock.readLock().unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the value of the secret stored within Keeper Secrets Manager and
|
* Returns the value of the secret stored within Keeper Secrets Manager and
|
||||||
* represented by the given Keeper notation. Keeper notation locates the
|
* represented by the given Keeper notation. Keeper notation locates the
|
||||||
|
|||||||
+18
-6
@@ -370,7 +370,7 @@ public class KsmSecretService implements VaultSecretService {
|
|||||||
*
|
*
|
||||||
* @param connectable
|
* @param connectable
|
||||||
* The connectable to which the connection is being established. This
|
* The connectable to which the connection is being established. This
|
||||||
* is the conneciton which will be checked to see if user KSM configs
|
* is the connection which will be checked to see if user KSM configs
|
||||||
* are enabled.
|
* are enabled.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@@ -435,18 +435,30 @@ public class KsmSecretService implements VaultSecretService {
|
|||||||
|
|
||||||
// Retrieve and define server-specific tokens, if any
|
// Retrieve and define server-specific tokens, if any
|
||||||
String hostname = parameters.get("hostname");
|
String hostname = parameters.get("hostname");
|
||||||
if (hostname != null && !hostname.isEmpty())
|
if (hostname != null && !hostname.isEmpty()) {
|
||||||
|
KeeperRecord record = ksm.getRecordByHost(filter.filter(hostname));
|
||||||
|
KeeperRecord adminRecord = ksm.getLinkedAdminRecord(record);
|
||||||
|
KeeperRecord launchRecord = ksm.getLinkedLaunchRecord(record);
|
||||||
addRecordTokens(tokens, "KEEPER_SERVER_",
|
addRecordTokens(tokens, "KEEPER_SERVER_",
|
||||||
ksm.getRecordByHost(filter.filter(hostname)));
|
adminRecord != null ? adminRecord : record);
|
||||||
|
addRecordTokens(tokens, "KEEPER_SERVER_ADMIN_", adminRecord);
|
||||||
|
addRecordTokens(tokens, "KEEPER_SERVER_LAUNCH_", launchRecord);
|
||||||
|
}
|
||||||
|
|
||||||
// Tokens specific to RDP
|
// Tokens specific to RDP
|
||||||
if ("rdp".equals(config.getProtocol())) {
|
if ("rdp".equals(config.getProtocol())) {
|
||||||
|
|
||||||
// Retrieve and define gateway server-specific tokens, if any
|
// Retrieve and define gateway server-specific tokens, if any
|
||||||
String gatewayHostname = parameters.get("gateway-hostname");
|
String gatewayHostname = parameters.get("gateway-hostname");
|
||||||
if (gatewayHostname != null && !gatewayHostname.isEmpty())
|
if (gatewayHostname != null && !gatewayHostname.isEmpty()) {
|
||||||
|
KeeperRecord record = ksm.getRecordByHost(filter.filter(gatewayHostname));
|
||||||
|
KeeperRecord adminRecord = ksm.getLinkedAdminRecord(record);
|
||||||
|
KeeperRecord launchRecord = ksm.getLinkedLaunchRecord(record);
|
||||||
addRecordTokens(tokens, "KEEPER_GATEWAY_",
|
addRecordTokens(tokens, "KEEPER_GATEWAY_",
|
||||||
ksm.getRecordByHost(filter.filter(gatewayHostname)));
|
adminRecord != null ? adminRecord : record);
|
||||||
|
addRecordTokens(tokens, "KEEPER_GATEWAY_ADMIN_", adminRecord);
|
||||||
|
addRecordTokens(tokens, "KEEPER_GATEWAY_LAUNCH_", launchRecord);
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve and define domain tokens, if any
|
// Retrieve and define domain tokens, if any
|
||||||
String domain = parameters.get("domain");
|
String domain = parameters.get("domain");
|
||||||
@@ -518,7 +530,7 @@ public class KsmSecretService implements VaultSecretService {
|
|||||||
config, getClient(userKsmConfig), tokens, parameters, filter);
|
config, getClient(userKsmConfig), tokens, parameters, filter);
|
||||||
|
|
||||||
// Add connection group or globally defined tokens after the user-specific
|
// Add connection group or globally defined tokens after the user-specific
|
||||||
// ones to ensure that the user config will be overriden on collision
|
// ones to ensure that the user config will be overridden on collision
|
||||||
String ksmConfig = getConnectionGroupKsmConfig(userContext, connectable);
|
String ksmConfig = getConnectionGroupKsmConfig(userContext, connectable);
|
||||||
addConnectableTokens(
|
addConnectableTokens(
|
||||||
config, getClient(ksmConfig), tokens, parameters, filter);
|
config, getClient(ksmConfig), tokens, parameters, filter);
|
||||||
|
|||||||
Reference in new issue
Block a user