@Nonnull
    private String getConnectionGroupKsmConfig(
            UserContext userContext, Connectable connectable) throws GuacamoleException {

        // Check to make sure it's a usable type before proceeding
        if (
                !(connectable instanceof Connection)
                && !(connectable instanceof ConnectionGroup)) {
            logger.warn(
                    "Unsupported Connectable type: {}; skipping KSM config lookup.",
                    connectable.getClass());

            // Use the default value if searching is impossible
            return confService.getKsmConfig();
        }

        // For connections, start searching the parent group for the KSM config
        // For connection groups, start searching the group directly
        String parentIdentifier = (connectable instanceof Connection)
                ? ((Connection) connectable).getParentIdentifier()
                : ((ConnectionGroup) connectable).getIdentifier();

        // Keep track of all group identifiers seen while recursing up the tree
        // in case there's a cycle - if the same identifier is ever seen twice,
        // the search is over.
        Set<String> observedIdentifiers = new HashSet<>();
        observedIdentifiers.add(parentIdentifier);

        // Use the unwrapped connection group directory to avoid KSM config
        // value sanitization
        Directory<ConnectionGroup> connectionGroupDirectory = (
                (KsmDirectory<ConnectionGroup>) userContext.getConnectionGroupDirectory()
                ).getUnderlyingDirectory();

        while (true) {

            // Fetch the parent group, if one exists
            ConnectionGroup group = connectionGroupDirectory.get(parentIdentifier);
            if (group == null)
                break;

            // If the current connection group has the KSM configuration attribute
            // set to a non-empty value, return immediately
            String ksmConfig = group.getAttributes().get(KsmAttributeService.KSM_CONFIGURATION_ATTRIBUTE);
            if (ksmConfig != null && !ksmConfig.trim().isEmpty())
                return ksmConfig;

            // Otherwise, keep searching up the tree until an appropriate configuration is found
            parentIdentifier = group.getParentIdentifier();

            // If the parent is a group that's already been seen, this is a cycle, so there's no
            // need to search any further
            if (!observedIdentifiers.add(parentIdentifier))
                break;
        }

        // If no KSM configuration was ever found, use the default value
        return confService.getKsmConfig();

    }
Search for a KSM configuration attribute, recursing up the connection group tree
until a connection group with the appropriate attribute is found. If the KSM config
is found, it will be returned. If not, the default value from the config file will
be returned.

@param userContext
    The userContext associated with the connection or connection group.

@param connectable
    A connection or connection group for which the tokens are being replaced.

@return
    The value of the KSM configuration attribute if found in the tree, the default
    KSM config blob defined in guacamole.properties otherwise.

@throws GuacamoleException
    If an error occurs while attempting to retrieve the KSM config attribute, or if
    no KSM config is found in the connection group tree, and the value is also not
    defined in the config file.
extensions/guacamole-vault/modules/guacamole-vault-ksm/src/main/java/org/apache/guacamole/vault/ksm/secret/KsmSecretService.java