| /* |
| * Copyright (C) 2020 Thomas Wolf <thomas.wolf@paranor.ch> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| package org.eclipse.jgit.transport.sshd; |
| |
| import java.io.File; |
| import java.nio.file.Path; |
| import java.security.KeyPair; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.function.BiFunction; |
| import java.util.function.Function; |
| |
| import org.eclipse.jgit.annotations.NonNull; |
| import org.eclipse.jgit.transport.CredentialsProvider; |
| import org.eclipse.jgit.transport.SshConfigStore; |
| import org.eclipse.jgit.util.StringUtils; |
| |
| /** |
| * A builder API to configure {@link SshdSessionFactory SshdSessionFactories}. |
| * |
| * @since 5.8 |
| */ |
| public final class SshdSessionFactoryBuilder { |
| |
| private final State state = new State(); |
| |
| /** |
| * Sets the {@link ProxyDataFactory} to use for {@link SshdSessionFactory |
| * SshdSessionFactories} created by {@link #build(KeyCache)}. |
| * |
| * @param proxyDataFactory |
| * to use |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setProxyDataFactory( |
| ProxyDataFactory proxyDataFactory) { |
| this.state.proxyDataFactory = proxyDataFactory; |
| return this; |
| } |
| |
| /** |
| * Sets the home directory to use for {@link SshdSessionFactory |
| * SshdSessionFactories} created by {@link #build(KeyCache)}. |
| * |
| * @param homeDirectory |
| * to use; may be {@code null}, in which case the home directory |
| * as defined by {@link org.eclipse.jgit.util.FS#userHome() |
| * FS.userHome()} is assumed |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setHomeDirectory(File homeDirectory) { |
| this.state.homeDirectory = homeDirectory; |
| return this; |
| } |
| |
| /** |
| * Sets the SSH directory to use for {@link SshdSessionFactory |
| * SshdSessionFactories} created by {@link #build(KeyCache)}. |
| * |
| * @param sshDirectory |
| * to use; may be {@code null}, in which case ".ssh" under the |
| * {@link #setHomeDirectory(File) home directory} is assumed |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setSshDirectory(File sshDirectory) { |
| this.state.sshDirectory = sshDirectory; |
| return this; |
| } |
| |
| /** |
| * Sets the default preferred authentication mechanisms to use for |
| * {@link SshdSessionFactory SshdSessionFactories} created by |
| * {@link #build(KeyCache)}. |
| * |
| * @param authentications |
| * comma-separated list of authentication mechanism names; if |
| * {@code null} or empty, the default as specified by |
| * {@link SshdSessionFactory#getDefaultPreferredAuthentications()} |
| * will be used |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setPreferredAuthentications( |
| String authentications) { |
| this.state.preferredAuthentications = authentications; |
| return this; |
| } |
| |
| /** |
| * Sets a function that returns the SSH config file, given the SSH |
| * directory. The function may return {@code null}, in which case no SSH |
| * config file will be used. If a non-null file is returned, it will be used |
| * when it exists. If no supplier has been set, or the supplier has been set |
| * explicitly to {@code null}, by default a file named |
| * {@link org.eclipse.jgit.transport.SshConstants#CONFIG |
| * SshConstants.CONFIG} in the {@link #setSshDirectory(File) SSH directory} |
| * is used. |
| * |
| * @param supplier |
| * returning a {@link File} for the SSH config file to use, or |
| * returning {@code null} if no config file is to be used |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setConfigFile( |
| Function<File, File> supplier) { |
| this.state.configFileFinder = supplier; |
| return this; |
| } |
| |
| /** |
| * A factory interface for creating a @link SshConfigStore}. |
| */ |
| @FunctionalInterface |
| public interface ConfigStoreFactory { |
| |
| /** |
| * Creates a {@link SshConfigStore}. May return {@code null} if none is |
| * to be used. |
| * |
| * @param homeDir |
| * to use for ~-replacements |
| * @param configFile |
| * to use, may be {@code null} if none |
| * @param localUserName |
| * name of the current user in the local OS |
| * @return the {@link SshConfigStore}, or {@code null} if none is to be |
| * used |
| */ |
| SshConfigStore create(@NonNull File homeDir, File configFile, |
| String localUserName); |
| } |
| |
| /** |
| * Sets a factory for the {@link SshConfigStore} to use. If not set or |
| * explicitly set to {@code null}, the default as specified by |
| * {@link SshdSessionFactory#createSshConfigStore(File, File, String)} is |
| * used. |
| * |
| * @param factory |
| * to set |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setConfigStoreFactory( |
| ConfigStoreFactory factory) { |
| this.state.configFactory = factory; |
| return this; |
| } |
| |
| /** |
| * Sets a function that returns the default known hosts files, given the SSH |
| * directory. If not set or explicitly set to {@code null}, the defaults as |
| * specified by {@link SshdSessionFactory#getDefaultKnownHostsFiles(File)} |
| * are used. |
| * |
| * @param supplier |
| * to get the default known hosts files |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setDefaultKnownHostsFiles( |
| Function<File, List<Path>> supplier) { |
| this.state.knownHostsFileFinder = supplier; |
| return this; |
| } |
| |
| /** |
| * Sets a function that returns the default private key files, given the SSH |
| * directory. If not set or explicitly set to {@code null}, the defaults as |
| * specified by {@link SshdSessionFactory#getDefaultIdentities(File)} are |
| * used. |
| * |
| * @param supplier |
| * to get the default private key files |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setDefaultIdentities( |
| Function<File, List<Path>> supplier) { |
| this.state.defaultKeyFileFinder = supplier; |
| return this; |
| } |
| |
| /** |
| * Sets a function that returns the default private keys, given the SSH |
| * directory. If not set or explicitly set to {@code null}, the defaults as |
| * specified by {@link SshdSessionFactory#getDefaultKeys(File)} are used. |
| * |
| * @param provider |
| * to get the default private key files |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setDefaultKeysProvider( |
| Function<File, Iterable<KeyPair>> provider) { |
| this.state.defaultKeysProvider = provider; |
| return this; |
| } |
| |
| /** |
| * Sets a factory function to create a {@link KeyPasswordProvider}. If not |
| * set or explicitly set to {@code null}, or if the factory returns |
| * {@code null}, the default as specified by |
| * {@link SshdSessionFactory#createKeyPasswordProvider(CredentialsProvider)} |
| * is used. |
| * |
| * @param factory |
| * to create a {@link KeyPasswordProvider} |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setKeyPasswordProvider( |
| Function<CredentialsProvider, KeyPasswordProvider> factory) { |
| this.state.passphraseProviderFactory = factory; |
| return this; |
| } |
| |
| /** |
| * Sets a function that creates a new {@link ServerKeyDatabase}, given the |
| * SSH and home directory. If not set or explicitly set to {@code null}, or |
| * if the {@code factory} returns {@code null}, the default as specified by |
| * {@link SshdSessionFactory#createServerKeyDatabase(File, File)} is used. |
| * |
| * @param factory |
| * to create a {@link ServerKeyDatabase} |
| * @return this {@link SshdSessionFactoryBuilder} |
| */ |
| public SshdSessionFactoryBuilder setServerKeyDatabase( |
| BiFunction<File, File, ServerKeyDatabase> factory) { |
| this.state.serverKeyDatabaseCreator = factory; |
| return this; |
| } |
| |
| /** |
| * Builds a {@link SshdSessionFactory} as configured, using the given |
| * {@link KeyCache} for caching keys. |
| * <p> |
| * Different {@link SshdSessionFactory SshdSessionFactories} should |
| * <em>not</em> share the same {@link KeyCache} since the cache is |
| * invalidated when the factory itself or when the last {@link SshdSession} |
| * created from the factory is closed. |
| * </p> |
| * |
| * @param cache |
| * to use for caching ssh keys; may be {@code null} if no caching |
| * is desired. |
| * @return the {@link SshdSessionFactory} |
| */ |
| public SshdSessionFactory build(KeyCache cache) { |
| // Use a copy to avoid that subsequent calls to setters affect an |
| // already created SshdSessionFactory. |
| return state.copy().build(cache); |
| } |
| |
| private static class State { |
| |
| ProxyDataFactory proxyDataFactory; |
| |
| File homeDirectory; |
| |
| File sshDirectory; |
| |
| String preferredAuthentications; |
| |
| Function<File, File> configFileFinder; |
| |
| ConfigStoreFactory configFactory; |
| |
| Function<CredentialsProvider, KeyPasswordProvider> passphraseProviderFactory; |
| |
| Function<File, List<Path>> knownHostsFileFinder; |
| |
| Function<File, List<Path>> defaultKeyFileFinder; |
| |
| Function<File, Iterable<KeyPair>> defaultKeysProvider; |
| |
| BiFunction<File, File, ServerKeyDatabase> serverKeyDatabaseCreator; |
| |
| State copy() { |
| State c = new State(); |
| c.proxyDataFactory = proxyDataFactory; |
| c.homeDirectory = homeDirectory; |
| c.sshDirectory = sshDirectory; |
| c.preferredAuthentications = preferredAuthentications; |
| c.configFileFinder = configFileFinder; |
| c.configFactory = configFactory; |
| c.passphraseProviderFactory = passphraseProviderFactory; |
| c.knownHostsFileFinder = knownHostsFileFinder; |
| c.defaultKeyFileFinder = defaultKeyFileFinder; |
| c.defaultKeysProvider = defaultKeysProvider; |
| c.serverKeyDatabaseCreator = serverKeyDatabaseCreator; |
| return c; |
| } |
| |
| SshdSessionFactory build(KeyCache cache) { |
| SshdSessionFactory factory = new SessionFactory(cache, |
| proxyDataFactory); |
| factory.setHomeDirectory(homeDirectory); |
| factory.setSshDirectory(sshDirectory); |
| return factory; |
| } |
| |
| private class SessionFactory extends SshdSessionFactory { |
| |
| public SessionFactory(KeyCache cache, |
| ProxyDataFactory proxyDataFactory) { |
| super(cache, proxyDataFactory); |
| } |
| |
| @Override |
| protected File getSshConfig(File sshDir) { |
| if (configFileFinder != null) { |
| return configFileFinder.apply(sshDir); |
| } |
| return super.getSshConfig(sshDir); |
| } |
| |
| @Override |
| protected List<Path> getDefaultKnownHostsFiles(File sshDir) { |
| if (knownHostsFileFinder != null) { |
| List<Path> result = knownHostsFileFinder.apply(sshDir); |
| return result == null ? Collections.emptyList() : result; |
| } |
| return super.getDefaultKnownHostsFiles(sshDir); |
| } |
| |
| @Override |
| protected List<Path> getDefaultIdentities(File sshDir) { |
| if (defaultKeyFileFinder != null) { |
| List<Path> result = defaultKeyFileFinder.apply(sshDir); |
| return result == null ? Collections.emptyList() : result; |
| } |
| return super.getDefaultIdentities(sshDir); |
| } |
| |
| @Override |
| protected String getDefaultPreferredAuthentications() { |
| if (!StringUtils.isEmptyOrNull(preferredAuthentications)) { |
| return preferredAuthentications; |
| } |
| return super.getDefaultPreferredAuthentications(); |
| } |
| |
| @Override |
| protected Iterable<KeyPair> getDefaultKeys(File sshDir) { |
| if (defaultKeysProvider != null) { |
| Iterable<KeyPair> result = defaultKeysProvider |
| .apply(sshDir); |
| return result == null ? Collections.emptyList() : result; |
| } |
| return super.getDefaultKeys(sshDir); |
| } |
| |
| @Override |
| protected KeyPasswordProvider createKeyPasswordProvider( |
| CredentialsProvider provider) { |
| if (passphraseProviderFactory != null) { |
| KeyPasswordProvider result = passphraseProviderFactory |
| .apply(provider); |
| if (result != null) { |
| return result; |
| } |
| } |
| return super.createKeyPasswordProvider(provider); |
| } |
| |
| @Override |
| protected ServerKeyDatabase createServerKeyDatabase(File homeDir, |
| File sshDir) { |
| if (serverKeyDatabaseCreator != null) { |
| ServerKeyDatabase result = serverKeyDatabaseCreator |
| .apply(homeDir, sshDir); |
| if (result != null) { |
| return result; |
| } |
| } |
| return super.createServerKeyDatabase(homeDir, sshDir); |
| } |
| |
| @Override |
| protected SshConfigStore createSshConfigStore(File homeDir, |
| File configFile, String localUserName) { |
| if (configFactory != null) { |
| return configFactory.create(homeDir, configFile, |
| localUserName); |
| } |
| return super.createSshConfigStore(homeDir, configFile, |
| localUserName); |
| } |
| } |
| } |
| } |