diff --git a/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/secret/AzureKeyVaultSecretService.java b/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/secret/AzureKeyVaultSecretService.java index cf9faa4c3..07337eb12 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/secret/AzureKeyVaultSecretService.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-azure/src/main/java/org/apache/guacamole/vault/azure/secret/AzureKeyVaultSecretService.java @@ -26,10 +26,14 @@ import com.microsoft.azure.keyvault.KeyVaultClient; import com.microsoft.azure.keyvault.authentication.KeyVaultCredentials; import com.microsoft.azure.keyvault.models.SecretBundle; import com.microsoft.rest.ServiceCallback; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.vault.azure.conf.AzureKeyVaultAuthenticationException; import org.apache.guacamole.vault.azure.conf.AzureKeyVaultConfigurationService; import org.apache.guacamole.vault.secret.CachedVaultSecretService; @@ -118,4 +122,10 @@ public class AzureKeyVaultSecretService extends CachedVaultSecretService { } + @Override + public Map> getTokens(GuacamoleConfiguration config) + throws GuacamoleException { + return Collections.emptyMap(); + } + } diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/secret/VaultSecretService.java b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/secret/VaultSecretService.java index 1ff230ca5..4cc5bb4a8 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/secret/VaultSecretService.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/secret/VaultSecretService.java @@ -19,8 +19,10 @@ package org.apache.guacamole.vault.secret; +import java.util.Map; import java.util.concurrent.Future; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; /** * Generic service for retrieving the value of a secret stored in a vault. @@ -69,4 +71,29 @@ public interface VaultSecretService { */ Future getValue(String name) throws GuacamoleException; + /** + * Returns a map of token names to corresponding Futures which eventually + * complete with the value of that token, where each token is dynamically + * defined based on connection parameters. If a vault implementation allows + * for predictable secrets based on the parameters of a connection, this + * function should be implemented to provide automatic tokens for those + * secrets and remove the need for manual mapping via YAML. + * + * @param config + * The configuration of the Guacamole connection for which tokens are + * being generated. This configuration may be empty or partial, + * depending on the underlying implementation. + * + * @return + * A map of token names to their corresponding future values, where + * each token and value may be dynamically determined based on the + * connection configuration. + * + * @throws GuacamoleException + * If an error occurs producing the tokens and values required for the + * given configuration. + */ + Map> getTokens(GuacamoleConfiguration config) + throws GuacamoleException; + } diff --git a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/user/VaultUserContext.java b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/user/VaultUserContext.java index 1367ac438..6df30fdad 100644 --- a/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/user/VaultUserContext.java +++ b/extensions/guacamole-vault/modules/guacamole-vault-base/src/main/java/org/apache/guacamole/vault/user/VaultUserContext.java @@ -32,6 +32,7 @@ import org.apache.guacamole.net.auth.Connection; import org.apache.guacamole.net.auth.ConnectionGroup; import org.apache.guacamole.net.auth.TokenInjectingUserContext; import org.apache.guacamole.net.auth.UserContext; +import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.token.GuacamoleTokenUndefinedException; import org.apache.guacamole.token.TokenFilter; import org.apache.guacamole.vault.conf.VaultConfigurationService; @@ -188,6 +189,10 @@ public class VaultUserContext extends TokenInjectingUserContext { * may contain its own tokens, which will be substituted using values * from the given filter. * + * @param config + * The GuacamoleConfiguration of the connection for which tokens are + * being retrieved, if available. This may be null. + * * @param filter * The filter to use to substitute values for tokens in the names of * secrets to be retrieved from the vault. @@ -202,7 +207,8 @@ public class VaultUserContext extends TokenInjectingUserContext { * vault due to an error. */ private Map> getTokens(Map tokenMapping, - TokenFilter filter) throws GuacamoleException { + GuacamoleConfiguration config, TokenFilter filter) + throws GuacamoleException { // Populate map with pending secret retrieval operations corresponding // to each mapped token @@ -230,6 +236,9 @@ public class VaultUserContext extends TokenInjectingUserContext { } + // Additionally include any dynamic, parameter-based tokens + pendingTokens.putAll(secretService.getTokens(config)); + return pendingTokens; } @@ -304,28 +313,28 @@ public class VaultUserContext extends TokenInjectingUserContext { // Substitute tokens producing secret names, retrieving and storing // those secrets as parameter tokens - return resolve(getTokens(confService.getTokenMapping(), filter)); + return resolve(getTokens(confService.getTokenMapping(), null, filter)); } /** - * Retrieves the connection parameters associated with the - * GuacamoleConfiguration of the given Connection. If possible, privileged - * access to those parameters is obtained first. Note that the underlying - * extension is not required to allow privileged access, nor is it - * required to expose the underlying connection parameters at all. + * Retrieves the GuacamoleConfiguration of the given Connection. If + * possible, privileged access to the configuration is obtained first. Note + * that the underlying extension is not required to allow privileged + * access, nor is it required to expose the underlying configuration at + * all. * * @param connection - * The connection to retrieve parameters from. + * The connection to retrieve the configuration from. * * @return - * A Map of all connection parameters exposed by the underlying - * extension for the given connection, which may be empty. + * The GuacamoleConfiguration associated with the given connection, + * which may be partial or empty. * * @throws GuacamoleException - * If an error prevents privileged retrieval of parameters. + * If an error prevents privileged retrieval of the configuration. */ - private Map getConnectionParameters(Connection connection) + private GuacamoleConfiguration getConnectionConfiguration(Connection connection) throws GuacamoleException { String identifier = connection.getIdentifier(); @@ -335,11 +344,11 @@ public class VaultUserContext extends TokenInjectingUserContext { // actually be privileged) Connection privilegedConnection = getPrivileged().getConnectionDirectory().get(identifier); if (privilegedConnection != null) - return privilegedConnection.getConfiguration().getParameters(); + return privilegedConnection.getConfiguration(); // Fall back to unprivileged access if not implemented/allowed by // extension - return connection.getConfiguration().getParameters(); + return connection.getConfiguration(); } @@ -360,7 +369,8 @@ public class VaultUserContext extends TokenInjectingUserContext { // Add hostname and username tokens if available (implementations are // not required to expose connection configuration details) - Map parameters = getConnectionParameters(connection); + GuacamoleConfiguration config = getConnectionConfiguration(connection); + Map parameters = config.getParameters(); String hostname = parameters.get("hostname"); if (hostname != null && !hostname.isEmpty()) @@ -382,7 +392,7 @@ public class VaultUserContext extends TokenInjectingUserContext { // Substitute tokens producing secret names, retrieving and storing // those secrets as parameter tokens - return resolve(getTokens(confService.getTokenMapping(), filter)); + return resolve(getTokens(confService.getTokenMapping(), config, filter)); } 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 c8ae0d7e3..42baff445 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 @@ -23,9 +23,12 @@ import com.google.inject.Inject; import com.google.inject.Singleton; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.util.Collections; +import java.util.Map; import java.util.concurrent.Future; import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.protocol.GuacamoleConfiguration; import org.apache.guacamole.vault.secret.VaultSecretService; /** @@ -59,4 +62,11 @@ public class KsmSecretService implements VaultSecretService { return ksm.getSecret(name); } + @Override + public Map> getTokens(GuacamoleConfiguration config) + throws GuacamoleException { + // STUB + return Collections.emptyMap(); + } + }