diff --git a/doc/licenses/kotlinx-serialization-1.7.2/dep-coordinates.txt b/doc/licenses/kotlinx-serialization-1.7.2/dep-coordinates.txt deleted file mode 100644 index e5f152853..000000000 --- a/doc/licenses/kotlinx-serialization-1.7.2/dep-coordinates.txt +++ /dev/null @@ -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 diff --git a/doc/licenses/kotlinx-serialization-1.7.2/NOTICE.txt b/doc/licenses/kotlinx-serialization-1.9.0/NOTICE.txt similarity index 100% rename from doc/licenses/kotlinx-serialization-1.7.2/NOTICE.txt rename to doc/licenses/kotlinx-serialization-1.9.0/NOTICE.txt diff --git a/doc/licenses/kotlinx-serialization-1.7.2/README b/doc/licenses/kotlinx-serialization-1.9.0/README similarity index 92% rename from doc/licenses/kotlinx-serialization-1.7.2/README rename to doc/licenses/kotlinx-serialization-1.9.0/README index 31687c5e0..2a0b0e982 100644 --- a/doc/licenses/kotlinx-serialization-1.7.2/README +++ b/doc/licenses/kotlinx-serialization-1.9.0/README @@ -1,7 +1,7 @@ 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' License(s): Apache v2.0 diff --git a/doc/licenses/kotlinx-serialization-1.9.0/dep-coordinates.txt b/doc/licenses/kotlinx-serialization-1.9.0/dep-coordinates.txt new file mode 100644 index 000000000..47ab20fe0 --- /dev/null +++ b/doc/licenses/kotlinx-serialization-1.9.0/dep-coordinates.txt @@ -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 diff --git a/doc/licenses/ksm-sdk-17.0.0/dep-coordinates.txt b/doc/licenses/ksm-sdk-17.0.0/dep-coordinates.txt deleted file mode 100644 index bf11a8fef..000000000 --- a/doc/licenses/ksm-sdk-17.0.0/dep-coordinates.txt +++ /dev/null @@ -1 +0,0 @@ -com.keepersecurity.secrets-manager:core:jar:17.0.0 diff --git a/doc/licenses/ksm-sdk-17.0.0/LICENSE b/doc/licenses/ksm-sdk-17.1.1/LICENSE similarity index 100% rename from doc/licenses/ksm-sdk-17.0.0/LICENSE rename to doc/licenses/ksm-sdk-17.1.1/LICENSE diff --git a/doc/licenses/ksm-sdk-17.0.0/README b/doc/licenses/ksm-sdk-17.1.1/README similarity index 77% rename from doc/licenses/ksm-sdk-17.0.0/README rename to doc/licenses/ksm-sdk-17.1.1/README index e84f0996b..ec676ea47 100644 --- a/doc/licenses/ksm-sdk-17.0.0/README +++ b/doc/licenses/ksm-sdk-17.1.1/README @@ -2,8 +2,8 @@ Keeper Secrets Manager Java SDK (https://github.com/Keeper-Security/secrets-manager) ---------------------------------------------------- - Version: 17.0.0 + Version: 17.1.1 From: 'Keeper Security' (https://www.keepersecurity.com/) License(s): - MIT (bundled/ksm-sdk-17.0.0/LICENSE) + MIT (bundled/ksm-sdk-17.1.1/LICENSE) diff --git a/doc/licenses/ksm-sdk-17.1.1/dep-coordinates.txt b/doc/licenses/ksm-sdk-17.1.1/dep-coordinates.txt new file mode 100644 index 000000000..ec260cdf4 --- /dev/null +++ b/doc/licenses/ksm-sdk-17.1.1/dep-coordinates.txt @@ -0,0 +1 @@ +com.keepersecurity.secrets-manager:core:jar:17.1.1 diff --git a/extensions/guacamole-vault/modules/guacamole-vault-ksm/pom.xml b/extensions/guacamole-vault/modules/guacamole-vault-ksm/pom.xml index 1eb1fd632..4a5367cfe 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-ksm/pom.xml +++ b/extensions/guacamole-vault/modules/guacamole-vault-ksm/pom.xml @@ -60,7 +60,7 @@ com.keepersecurity.secrets-manager core - 17.0.0 + 17.1.1 diff --git a/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmClient.java b/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmClient.java index fd31efa24..f811e9421 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmClient.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmClient.java @@ -24,9 +24,11 @@ import com.google.inject.assistedinject.Assisted; import com.google.inject.assistedinject.AssistedInject; import com.keepersecurity.secretsManager.core.Hosts; import com.keepersecurity.secretsManager.core.KeeperRecord; +import com.keepersecurity.secretsManager.core.KeeperRecordLink; import com.keepersecurity.secretsManager.core.KeeperSecrets; import com.keepersecurity.secretsManager.core.Login; import com.keepersecurity.secretsManager.core.Notation; +import com.keepersecurity.secretsManager.core.QueryOptions; import com.keepersecurity.secretsManager.core.SecretsManager; import com.keepersecurity.secretsManager.core.SecretsManagerOptions; @@ -156,6 +158,22 @@ public class KsmClient { */ private final Map 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 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 cachedLinkedLaunchRecords = new HashMap<>(); + /** * The set of all hostnames or IP addresses that are associated with * multiple records, and thus cannot uniquely identify a record. The @@ -264,9 +282,16 @@ public class KsmClient { if (currentTime - cacheTimestamp < cacheInterval) return; + // Enable GraphSync + QueryOptions queryOptions = new QueryOptions( + Collections.emptyList(), + Collections.emptyList(), + true + ); + // Attempt to pull all records first, allowing that operation to // succeed/fail BEFORE we clear out the last cached success - KeeperSecrets secrets = SecretsManager.getSecrets(ksmConfig); + KeeperSecrets secrets = SecretsManager.getSecrets2(ksmConfig, queryOptions); List records = secrets.getRecords(); // Store all secrets within cache @@ -275,6 +300,10 @@ public class KsmClient { // Clear unambiguous cache of all records by UID cachedRecordsByUid.clear(); + // Clear cache of all linked records + cachedLinkedAdminRecords.clear(); + cachedLinkedLaunchRecords.clear(); + // Clear cache of host-based records cachedAmbiguousHosts.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 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 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. * 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 * represented by the given Keeper notation. Keeper notation locates the diff --git a/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmSecretService.java b/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmSecretService.java index 632d597fa..9ed606cd7 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmSecretService.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmSecretService.java @@ -370,7 +370,7 @@ public class KsmSecretService implements VaultSecretService { * * @param connectable * 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. * * @return @@ -435,18 +435,30 @@ public class KsmSecretService implements VaultSecretService { // Retrieve and define server-specific tokens, if any 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_", - 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 if ("rdp".equals(config.getProtocol())) { // Retrieve and define gateway server-specific tokens, if any 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_", - 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 String domain = parameters.get("domain"); @@ -518,7 +530,7 @@ public class KsmSecretService implements VaultSecretService { config, getClient(userKsmConfig), tokens, parameters, filter); // 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); addConnectableTokens( config, getClient(ksmConfig), tokens, parameters, filter);