Github user necouchman commented on a diff in the pull request:
https://github.com/apache/guacamole-client/pull/336#discussion_r240048911
--- Diff:
extensions/guacamole-auth-vault/modules/guacamole-auth-vault-base/src/main/java/org/apache/guacamole/auth/vault/user/VaultUserContext.java
---
@@ -0,0 +1,325 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.guacamole.auth.vault.user;
+
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+import com.google.inject.assistedinject.AssistedInject;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import org.apache.guacamole.GuacamoleException;
+import org.apache.guacamole.GuacamoleServerException;
+import org.apache.guacamole.auth.vault.conf.VaultConfigurationService;
+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.auth.vault.secret.VaultSecretService;
+import org.apache.guacamole.protocol.GuacamoleConfiguration;
+import org.apache.guacamole.token.GuacamoleTokenUndefinedException;
+import org.apache.guacamole.token.TokenFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * UserContext implementation which automatically injects tokens
containing the
+ * values of secrets retrieved from a vault.
+ */
+public class VaultUserContext extends TokenInjectingUserContext {
+
+ /**
+ * Logger for this class.
+ */
+ private final Logger logger =
LoggerFactory.getLogger(VaultUserContext.class);
+
+ /**
+ * The name of the token which will be replaced with the username of
the
+ * current user if specified within the name of a secret. This token
+ * applies to both connections and connection groups.
+ */
+ private static final String USERNAME_TOKEN = "GUAC_USERNAME";
+
+ /**
+ * The name of the token which will be replaced with the name of the
+ * current connection group if specified within the name of a secret.
This
+ * token only applies only to connection groups.
+ */
+ private static final String CONNECTION_GROUP_NAME_TOKEN =
"CONNECTION_GROUP_NAME";
+
+ /**
+ * The name of the token which will be replaced with the identifier of
the
+ * current connection group if specified within the name of a secret.
This
+ * token only applies only to connection groups.
+ */
+ private static final String CONNECTION_GROUP_IDENTIFIER_TOKEN =
"CONNECTION_GROUP_ID";
+
+ /**
+ * The name of the token which will be replaced with the \"hostname\"
+ * connection parameter of the current connection if specified within
the
+ * name of a secret. This token only applies only to connections.
+ */
+ private static final String CONNECTION_HOSTNAME_TOKEN =
"CONNECTION_HOSTNAME";
+
+ /**
+ * The name of the token which will be replaced with the \"username\"
+ * connection parameter of the current connection if specified within
the
+ * name of a secret. This token only applies only to connections.
+ */
+ private static final String CONNECTION_USERNAME_TOKEN =
"CONNECTION_USERNAME";
+
+ /**
+ * The name of the token which will be replaced with the name of the
+ * current connection if specified within the name of a secret. This
token
+ * only applies only to connections.
+ */
+ private static final String CONNECTION_NAME_TOKEN = "CONNECTION_NAME";
+
+ /**
+ * The name of the token which will be replaced with the identifier of
the
+ * current connection if specified within the name of a secret. This
token
+ * only applies only to connections.
+ */
+ private static final String CONNECTION_IDENTIFIER_TOKEN =
"CONNECTION_ID";
+
+ /**
+ * Service for retrieving configuration information.
+ */
+ @Inject
+ private VaultConfigurationService confService;
+
+ /**
+ * Service for retrieving the values of secrets stored in a vault.
+ */
+ @Inject
+ private VaultSecretService secretService;
+
+ /**
+ * Creates a new VaultUserContext which automatically injects tokens
+ * containing values of secrets retrieved from a vault. The given
+ * UserContext is decorated such that connections and connection groups
+ * will receive additional tokens during the connection process.
+ *
+ * Note that this class depends on concrete implementations of the
+ * following classes to be provided via dependency injection:
+ *
+ * - VaultConfigurationService
+ * - VaultSecretService
+ *
+ * Bindings providing these concrete implementations will need to be
+ * provided by subclasses of VaultAuthenticationProviderModule for each
+ * supported vault.
+ *
+ * @param userContext
+ * The UserContext instance to decorate.
+ */
+ @AssistedInject
+ public VaultUserContext(@Assisted UserContext userContext) {
+ super(userContext);
+ }
+
+ /**
+ * Creates a new TokenFilter instance with token values set for all
tokens
+ * which are not specific to connections or connection groups.
Currently,
+ * this is only the username token ("GUAC_USERNAME").
+ *
+ * @return
+ * A new TokenFilter instance with token values set for all tokens
+ * which are not specific to connections or connection groups.
+ */
+ private TokenFilter createFilter() {
+ TokenFilter filter = new TokenFilter();
+ filter.setToken(USERNAME_TOKEN, self().getIdentifier());
+ return filter;
+ }
+
+ /**
+ * Initiates asynchronous retrieval of all applicable tokens and
+ * corresponding values from the vault, using the given TokenFilter to
+ * filter tokens within the secret names prior to retrieving those
secrets.
+ *
+ * @param tokenMapping
+ * The mapping dictating the name of the secret which maps to each
+ * parameter token, where the key is the name of the parameter
token
+ * and the value is the name of the secret. The name of the secret
+ * may contain its own tokens, which will be substituted using
values
+ * from the given filter.
+ *
+ * @param filter
+ * The filter to use to substitute values for tokens in the names
of
+ * secrets to be retrieved from the vault.
+ *
+ * @return
+ * A Map of token name to Future, where each Future represents the
+ * pending retrieval operation which will ultimately be completed
with
+ * the value of all secret mapped to that token.
+ *
+ * @throws GuacamoleException
+ * If the value for any applicable secret cannot be retrieved from
the
+ * vault due to an error.
+ */
+ private Map<String, Future<String>> getTokens(Map<String, String>
tokenMapping,
+ TokenFilter filter) throws GuacamoleException {
+
+ // Populate map with pending secret retrieval operations
corresponding
+ // to each mapped token
+ Map<String, Future<String>> pendingTokens = new
HashMap<>(tokenMapping.size());
+ for (Map.Entry<String, String> entry : tokenMapping.entrySet()) {
+
+ // Translate secret pattern into secret name, ignoring any
+ // secrets which cannot be translated
+ String secretName;
+ try {
+ secretName =
secretService.canonicalize(filter.filterStrict(entry.getValue()));
+ }
+ catch (GuacamoleTokenUndefinedException e) {
+ logger.debug("Secret for token \"{}\" will not be
retrieved. "
+ + "Token \"{}\" within mapped secret name has no "
+ + "defined value in the current context.",
+ entry.getKey(), e.getTokenName());
+ continue;
+ }
+
+ // Initiate asynchronous retrieval of the token value
+ String tokenName = entry.getKey();
+ Future<String> secret = secretService.getValue(secretName);
+ pendingTokens.put(tokenName, secret);
+
+ }
+
+ return pendingTokens;
+
+ }
+
+ /**
+ * Waits for all pending secret retrieval operations to complete,
+ * transforming each Future within the given Map into its contained
String
+ * value.
+ *
+ * @param pendingTokens
+ * A Map of token name to Future, where each Future represents the
+ * pending retrieval operation which will ultimately be completed
with
+ * the value of all secret mapped to that token.
+ *
--- End diff --
Missing `@return` tag.
---