[SSHD-771] Added capability to configure server-side MAC(s) via standard command line option
Project: http://git-wip-us.apache.org/repos/asf/mina-sshd/repo Commit: http://git-wip-us.apache.org/repos/asf/mina-sshd/commit/a65c2f27 Tree: http://git-wip-us.apache.org/repos/asf/mina-sshd/tree/a65c2f27 Diff: http://git-wip-us.apache.org/repos/asf/mina-sshd/diff/a65c2f27 Branch: refs/heads/master Commit: a65c2f272e1192a38fd720b5acc6a59147b3f0a2 Parents: e0a6b8d Author: Goldstein Lyor <l...@c-b4.com> Authored: Tue Sep 12 09:21:26 2017 +0300 Committer: Goldstein Lyor <l...@c-b4.com> Committed: Tue Sep 12 10:57:53 2017 +0300 ---------------------------------------------------------------------- .../java/org/apache/sshd/client/SshClient.java | 20 +- .../config/SshClientConfigFileReader.java | 40 ++++ .../sshd/common/PropertyResolverUtils.java | 27 ++- .../sshd/common/config/SshConfigFileReader.java | 189 +++---------------- .../java/org/apache/sshd/server/SshServer.java | 20 +- .../config/SshServerConfigFileReader.java | 21 ++- .../common/config/SshConfigFileReaderTest.java | 13 +- 7 files changed, 141 insertions(+), 189 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java index 876b2ca..ce4b192 100644 --- a/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java +++ b/sshd-core/src/main/java/org/apache/sshd/client/SshClient.java @@ -44,10 +44,10 @@ import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.TreeMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.ConsoleHandler; import java.util.logging.Formatter; @@ -91,6 +91,7 @@ import org.apache.sshd.common.Factory; import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.ServiceFactory; import org.apache.sshd.common.channel.Channel; @@ -800,7 +801,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa String password = null; boolean error = false; List<Path> identities = new ArrayList<>(); - Map<String, String> options = new LinkedHashMap<>(); + Map<String, Object> options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); List<NamedFactory<Cipher>> ciphers = null; List<NamedFactory<Mac>> macs = null; List<NamedFactory<Compression>> compressions = null; @@ -954,28 +955,29 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa // returns null if error encountered public static SshClient setupClient( - Map<String, ?> options, + Map<String, Object> options, List<NamedFactory<Cipher>> ciphers, List<NamedFactory<Mac>> macs, List<NamedFactory<Compression>> compressions, Collection<? extends Path> identities, BufferedReader stdin, PrintStream stdout, PrintStream stderr) throws Exception { + PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options); if (GenericUtils.isEmpty(ciphers)) { - ciphers = setupCiphers(options, stderr); + ciphers = setupCiphers(resolver, stderr); if (ciphers == null) { return null; } } if (GenericUtils.isEmpty(macs)) { - macs = setupMacs(options, stderr); + macs = setupMacs(resolver, stderr); if (macs == null) { return null; } } if (GenericUtils.isEmpty(compressions)) { - compressions = setupCompressions(options, stderr); + compressions = setupCompressions(resolver, stderr); if (compressions == null) { return null; } @@ -1177,7 +1179,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa return stderr; } - public static List<NamedFactory<Compression>> setupCompressions(Map<String, ?> options, PrintStream stderr) { + public static List<NamedFactory<Compression>> setupCompressions(PropertyResolver options, PrintStream stderr) { String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.COMPRESSION_PROP); if (GenericUtils.isEmpty(argVal)) { return Collections.emptyList(); @@ -1214,7 +1216,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa return new ArrayList<>(available); } - public static List<NamedFactory<Mac>> setupMacs(Map<String, ?> options, PrintStream stderr) { + public static List<NamedFactory<Mac>> setupMacs(PropertyResolver options, PrintStream stderr) { String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.MACS_CONFIG_PROP); return GenericUtils.isEmpty(argVal) ? Collections.emptyList() @@ -1242,7 +1244,7 @@ public class SshClient extends AbstractFactoryManager implements ClientFactoryMa return new ArrayList<>(available); } - public static List<NamedFactory<Cipher>> setupCiphers(Map<String, ?> options, PrintStream stderr) { + public static List<NamedFactory<Cipher>> setupCiphers(PropertyResolver options, PrintStream stderr) { String argVal = PropertyResolverUtils.getString(options, SshConfigFileReader.CIPHERS_CONFIG_PROP); return GenericUtils.isEmpty(argVal) ? Collections.emptyList() http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java new file mode 100644 index 0000000..ccd8f26 --- /dev/null +++ b/sshd-core/src/main/java/org/apache/sshd/client/config/SshClientConfigFileReader.java @@ -0,0 +1,40 @@ +/* + * 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.sshd.client.config; + +import org.apache.sshd.client.ClientBuilder; +import org.apache.sshd.client.SshClient; +import org.apache.sshd.common.PropertyResolver; +import org.apache.sshd.common.config.SshConfigFileReader; +import org.apache.sshd.common.helpers.AbstractFactoryManager; + +/** + * @author <a href="mailto:d...@mina.apache.org">Apache MINA SSHD Project</a> + */ +public final class SshClientConfigFileReader { + private SshClientConfigFileReader() { + throw new UnsupportedOperationException("No instance allowed"); + } + + public static <C extends SshClient> C configure(C client, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { + SshConfigFileReader.configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported); + SshConfigFileReader.configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported); + return client; + } +} http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java index 584668e..78ebea6 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/PropertyResolverUtils.java @@ -21,9 +21,12 @@ package org.apache.sshd.common; import java.nio.charset.Charset; import java.util.Collection; +import java.util.Comparator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.Properties; +import java.util.TreeMap; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; @@ -426,19 +429,37 @@ public final class PropertyResolverUtils { return null; } + public static PropertyResolver toPropertyResolver(Properties props) { + if (GenericUtils.isEmpty(props)) { + return PropertyResolver.EMPTY; + } + + Map<String, Object> propsMap = new TreeMap<>(Comparator.naturalOrder()); + Collection<String> names = props.stringPropertyNames(); + for (String key : names) { + String value = props.getProperty(key); + if (value == null) { + continue; + } + propsMap.put(key, value); + } + + return toPropertyResolver(propsMap); + } + /** * Wraps a {@link Map} into a {@link PropertyResolver} so it can be used * with these utilities * * @param props The properties map - may be {@code null}/empty if no properties - * are updated + * are updated * @return The resolver wrapper */ - public static PropertyResolver toPropertyResolver(final Map<String, Object> props) { + public static PropertyResolver toPropertyResolver(Map<String, Object> props) { return toPropertyResolver(props, null); } - public static PropertyResolver toPropertyResolver(final Map<String, Object> props, final PropertyResolver parent) { + public static PropertyResolver toPropertyResolver(Map<String, Object> props, PropertyResolver parent) { return new PropertyResolver() { @Override public PropertyResolver getParentPropertyResolver() { http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java index b6d87d4..4a93294 100644 --- a/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java +++ b/sshd-core/src/main/java/org/apache/sshd/common/config/SshConfigFileReader.java @@ -40,10 +40,9 @@ import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.function.Function; -import org.apache.sshd.client.ClientBuilder; -import org.apache.sshd.client.SshClient; import org.apache.sshd.common.BuiltinFactory; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.cipher.BuiltinCiphers; import org.apache.sshd.common.cipher.Cipher; import org.apache.sshd.common.compression.BuiltinCompressions; @@ -64,8 +63,6 @@ import org.apache.sshd.common.util.io.IoUtils; import org.apache.sshd.common.util.io.NoCloseInputStream; import org.apache.sshd.common.util.io.NoCloseReader; import org.apache.sshd.common.util.net.SshdSocketAddress; -import org.apache.sshd.server.ServerBuilder; -import org.apache.sshd.server.SshServer; /** * Reads and interprets some useful configurations from an OpenSSH @@ -228,120 +225,6 @@ public final class SshConfigFileReader { } /** - * @param props The {@link Properties} - ignored if {@code null}/empty - * @param name The property name - * @param defaultValue The default value to return if the specified property - * does not exist in the properties map or is an empty string - * @return The resolved property - * @throws NumberFormatException if malformed value - */ - public static long getLongProperty(Properties props, String name, long defaultValue) { - String value = (props == null) ? null : props.getProperty(name); - if (GenericUtils.isEmpty(value)) { - return defaultValue; - } else { - return Long.parseLong(value); - } - } - - /** - * @param props The {@link Properties} - ignored if {@code null}/empty - * @param name The property name - * @return The {@link Long} value or {@code null} if property not found or - * empty string - * @throws NumberFormatException if malformed value - */ - public static Long getLong(Properties props, String name) { - String value = (props == null) ? null : props.getProperty(name); - if (GenericUtils.isEmpty(value)) { - return null; - } else { - return Long.valueOf(value); - } - } - - /** - * @param props The {@link Properties} - ignored if {@code null}/empty - * @param name The property name - * @param defaultValue The default value to return if the specified property - * does not exist in the properties map or is an empty string - * @return The resolved property - * @throws NumberFormatException if malformed value - */ - public static int getIntProperty(Properties props, String name, int defaultValue) { - String value = (props == null) ? null : props.getProperty(name); - if (GenericUtils.isEmpty(value)) { - return defaultValue; - } else { - return Integer.parseInt(value); - } - } - - /** - * @param props The {@link Properties} - ignored if {@code null}/empty - * @param name The property name - * @return The {@link Integer} value or {@code null} if property not found or - * empty string - * @throws NumberFormatException if malformed value - */ - public static Integer getInteger(Properties props, String name) { - String value = (props == null) ? null : props.getProperty(name); - if (GenericUtils.isEmpty(value)) { - return null; - } else { - return Integer.valueOf(value); - } - } - - /** - * @param props The {@link Properties} - ignored if {@code null}/empty - * @param name The property name - * @param defaultValue The default value to return if the specified property - * does not exist in the properties map or is an empty string - * @return The resolved property - * @throws NumberFormatException if malformed value - */ - public static boolean getBooleanProperty(Properties props, String name, boolean defaultValue) { - String value = (props == null) ? null : props.getProperty(name); - if (GenericUtils.isEmpty(value)) { - return defaultValue; - } else { - return parseBooleanValue(value); - } - } - - /** - * @param props The {@link Properties} - ignored if {@code null}/empty - * @param name The property name - * @return The {@link Boolean} value or {@code null} if property not found or - * empty string - * @throws NumberFormatException if malformed value - */ - public static Boolean getBoolean(Properties props, String name) { - String value = (props == null) ? null : props.getProperty(name); - if (GenericUtils.isEmpty(value)) { - return null; - } else { - return parseBooleanValue(value); - } - } - - /** - * @param v The value to parse - if {@code null}/empty then the default - * value is returned, otherwise {@link #parseBooleanValue(String)} is used - * @param defaultValue The default value to return if {@code null}/empty - * input string - * @return The result - */ - public static boolean parseBooleanValue(String v, boolean defaultValue) { - if (GenericUtils.isEmpty(v)) { - return defaultValue; - } else { - return parseBooleanValue(v); - } - } - - /** * @param v Checks if the value is "yes", "y" * or "on" or "true". * @return The result - <B>Note:</B> {@code null}/empty values are @@ -366,7 +249,7 @@ public final class SshConfigFileReader { } /** - * @param props The {@link Properties} - ignored if {@code null}/empty + * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return A {@code ParseResult} of all the {@link NamedFactory}-ies * whose name appears in the string and represent a built-in cipher. * Any unknown name is <U>ignored</U>. The order of the returned result @@ -376,12 +259,12 @@ public final class SshConfigFileReader { * @see #CIPHERS_CONFIG_PROP * @see BuiltinCiphers#parseCiphersList(String) */ - public static BuiltinCiphers.ParseResult getCiphers(Properties props) { - return BuiltinCiphers.parseCiphersList((props == null) ? null : props.getProperty(CIPHERS_CONFIG_PROP)); + public static BuiltinCiphers.ParseResult getCiphers(PropertyResolver props) { + return BuiltinCiphers.parseCiphersList((props == null) ? null : props.getString(CIPHERS_CONFIG_PROP)); } /** - * @param props The {@link Properties} - ignored if {@code null}/empty + * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return A {@code ParseResult} of all the {@link NamedFactory}-ies * whose name appears in the string and represent a built-in MAC. Any * unknown name is <U>ignored</U>. The order of the returned result @@ -391,12 +274,12 @@ public final class SshConfigFileReader { * @see #MACS_CONFIG_PROP * @see BuiltinMacs#parseMacsList(String) */ - public static BuiltinMacs.ParseResult getMacs(Properties props) { - return BuiltinMacs.parseMacsList((props == null) ? null : props.getProperty(MACS_CONFIG_PROP)); + public static BuiltinMacs.ParseResult getMacs(PropertyResolver props) { + return BuiltinMacs.parseMacsList((props == null) ? null : props.getString(MACS_CONFIG_PROP)); } /** - * @param props The {@link Properties} - ignored if {@code null}/empty + * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return A {@code ParseResult} of all the {@link NamedFactory} * whose name appears in the string and represent a built-in signature. Any * unknown name is <U>ignored</U>. The order of the returned result is the @@ -405,12 +288,12 @@ public final class SshConfigFileReader { * @see #HOST_KEY_ALGORITHMS_CONFIG_PROP * @see BuiltinSignatures#parseSignatureList(String) */ - public static BuiltinSignatures.ParseResult getSignatures(Properties props) { - return BuiltinSignatures.parseSignatureList((props == null) ? null : props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP)); + public static BuiltinSignatures.ParseResult getSignatures(PropertyResolver props) { + return BuiltinSignatures.parseSignatureList((props == null) ? null : props.getString(HOST_KEY_ALGORITHMS_CONFIG_PROP)); } /** - * @param props The {@link Properties} - ignored if {@code null}/empty + * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return A {@code ParseResult} of all the {@link DHFactory}-ies * whose name appears in the string and represent a built-in value. Any * unknown name is <U>ignored</U>. The order of the returned result is the @@ -419,29 +302,17 @@ public final class SshConfigFileReader { * @see #KEX_ALGORITHMS_CONFIG_PROP * @see BuiltinDHFactories#parseDHFactoriesList(String) */ - public static BuiltinDHFactories.ParseResult getKexFactories(Properties props) { - return BuiltinDHFactories.parseDHFactoriesList((props == null) ? null : props.getProperty(KEX_ALGORITHMS_CONFIG_PROP)); + public static BuiltinDHFactories.ParseResult getKexFactories(PropertyResolver props) { + return BuiltinDHFactories.parseDHFactoriesList((props == null) ? null : props.getString(KEX_ALGORITHMS_CONFIG_PROP)); } /** - * @param props The {@link Properties} - ignored if {@code null}/empty + * @param props The {@link PropertyResolver} - ignored if {@code null}/empty * @return The matching {@link NamedFactory} for the configured value. * {@code null} if no configuration or unknown name specified */ - public static CompressionFactory getCompression(Properties props) { - return CompressionConfigValue.fromName((props == null) ? null : props.getProperty(COMPRESSION_PROP)); - } - - public static <S extends SshServer> S configure(S server, Properties props, boolean lenient, boolean ignoreUnsupported) { - configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported); - configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported); - return server; - } - - public static <C extends SshClient> C configure(C client, Properties props, boolean lenient, boolean ignoreUnsupported) { - configure((AbstractFactoryManager) client, props, lenient, ignoreUnsupported); - configureKeyExchanges(client, props, lenient, ClientBuilder.DH2KEX, ignoreUnsupported); - return client; + public static CompressionFactory getCompression(PropertyResolver props) { + return CompressionConfigValue.fromName((props == null) ? null : props.getString(COMPRESSION_PROP)); } /** @@ -456,7 +327,7 @@ public final class SshConfigFileReader { * * @param <M> The generic factory manager * @param manager The {@link AbstractFactoryManager} to configure - * @param props The {@link Properties} to use for configuration - <B>Note:</B> + * @param props The {@link PropertyResolver} to use for configuration - <B>Note:</B> * if any known configuration value has a default and does not appear in the * properties, the default is used * @param lenient If {@code true} then any unknown configuration values are ignored. @@ -466,7 +337,7 @@ public final class SshConfigFileReader { * or unsupported values there is an empty configuration exception is thrown * @return The configured manager */ - public static <M extends AbstractFactoryManager> M configure(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configure(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { configureCiphers(manager, props, lenient, ignoreUnsupported); configureSignatures(manager, props, lenient, ignoreUnsupported); configureMacs(manager, props, lenient, ignoreUnsupported); @@ -475,9 +346,9 @@ public final class SshConfigFileReader { return manager; } - public static <M extends AbstractFactoryManager> M configureCiphers(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureCiphers(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); - return configureCiphers(manager, props.getProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported); + return configureCiphers(manager, props.getStringProperty(CIPHERS_CONFIG_PROP, DEFAULT_CIPHERS), lenient, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureCiphers(M manager, String value, boolean lenient, boolean ignoreUnsupported) { @@ -493,9 +364,9 @@ public final class SshConfigFileReader { return manager; } - public static <M extends AbstractFactoryManager> M configureSignatures(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureSignatures(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); - return configureSignatures(manager, props.getProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported); + return configureSignatures(manager, props.getStringProperty(HOST_KEY_ALGORITHMS_CONFIG_PROP, DEFAULT_HOST_KEY_ALGORITHMS), lenient, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureSignatures(M manager, String value, boolean lenient, boolean ignoreUnsupported) { @@ -511,9 +382,9 @@ public final class SshConfigFileReader { return manager; } - public static <M extends AbstractFactoryManager> M configureMacs(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) { - Objects.requireNonNull(props, "No properties to configure"); - return configureMacs(manager, props.getProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported); + public static <M extends AbstractFactoryManager> M configureMacs(M manager, PropertyResolver resolver, boolean lenient, boolean ignoreUnsupported) { + Objects.requireNonNull(resolver, "No properties to configure"); + return configureMacs(manager, resolver.getStringProperty(MACS_CONFIG_PROP, DEFAULT_MACS), lenient, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureMacs(M manager, String value, boolean lenient, boolean ignoreUnsupported) { @@ -532,7 +403,7 @@ public final class SshConfigFileReader { /** * @param <M> The generic factory manager * @param manager The {@link AbstractFactoryManager} to set up (may not be {@code null}) - * @param props The (non-{@code null}) {@link Properties} containing the configuration + * @param props The (non-{@code null}) {@link PropertyResolver} containing the configuration * @param lenient If {@code true} then any unknown/unsupported configuration * values are ignored. Otherwise an {@link IllegalArgumentException} is thrown * @param xformer A {@link Function} to convert the configured {@link DHFactory}-ies @@ -545,9 +416,9 @@ public final class SshConfigFileReader { * @see #DEFAULT_KEX_ALGORITHMS */ public static <M extends AbstractFactoryManager> M configureKeyExchanges( - M manager, Properties props, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) { + M manager, PropertyResolver props, boolean lenient, Function<? super DHFactory, ? extends NamedFactory<KeyExchange>> xformer, boolean ignoreUnsupported) { Objects.requireNonNull(props, "No properties to configure"); - return configureKeyExchanges(manager, props.getProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported); + return configureKeyExchanges(manager, props.getStringProperty(KEX_ALGORITHMS_CONFIG_PROP, DEFAULT_KEX_ALGORITHMS), lenient, xformer, ignoreUnsupported); } public static <M extends AbstractFactoryManager> M configureKeyExchanges( @@ -578,11 +449,11 @@ public final class SshConfigFileReader { * @return The configured manager - <B>Note:</B> if the result of filtering due * to lenient mode or ignored unsupported value is empty then no factories are set */ - public static <M extends AbstractFactoryManager> M configureCompression(M manager, Properties props, boolean lenient, boolean ignoreUnsupported) { + public static <M extends AbstractFactoryManager> M configureCompression(M manager, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { Objects.requireNonNull(manager, "No manager to configure"); Objects.requireNonNull(props, "No properties to configure"); - String value = props.getProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION); + String value = props.getStringProperty(COMPRESSION_PROP, DEFAULT_COMPRESSION); CompressionFactory factory = CompressionConfigValue.fromName(value); ValidateUtils.checkTrue(lenient || (factory != null), "Unsupported compression value: %s", value); if ((factory != null) && factory.isSupported()) { http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java index 924c6ee..0985b2b 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/SshServer.java @@ -33,17 +33,19 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.TreeMap; import org.apache.sshd.common.Closeable; import org.apache.sshd.common.Factory; import org.apache.sshd.common.NamedFactory; +import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.ServiceFactory; +import org.apache.sshd.common.config.SshConfigFileReader; import org.apache.sshd.common.config.keys.BuiltinIdentities; import org.apache.sshd.common.config.keys.KeyUtils; import org.apache.sshd.common.helpers.AbstractFactoryManager; @@ -464,7 +466,7 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa String hostKeyType = AbstractGeneratorHostKeyProvider.DEFAULT_ALGORITHM; int hostKeySize = 0; Collection<String> keyFiles = null; - Map<String, String> options = new LinkedHashMap<>(); + Map<String, Object> options = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); int numArgs = GenericUtils.length(args); for (int i = 0; i < numArgs; i++) { @@ -576,16 +578,22 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa Map<String, Object> props = sshd.getProperties(); props.putAll(options); + PropertyResolver resolver = PropertyResolverUtils.toPropertyResolver(options); KeyPairProvider hostKeyProvider = setupServerKeys(sshd, hostKeyType, hostKeySize, keyFiles); sshd.setKeyPairProvider(hostKeyProvider); // Should come AFTER key pair provider setup so auto-welcome can be generated if needed - setupServerBanner(sshd, options); + setupServerBanner(sshd, resolver); sshd.setPort(port); + String macsOverride = resolver.getString(SshConfigFileReader.MACS_CONFIG_PROP); + if (GenericUtils.isNotEmpty(macsOverride)) { + SshConfigFileReader.configureMacs(sshd, macsOverride, true, true); + } + sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE); sshd.setPasswordAuthenticator((username, password, session) -> Objects.equals(username, password)); sshd.setPublickeyAuthenticator(AcceptAllPublickeyAuthenticator.INSTANCE); - setupServerForwarding(sshd, options); + setupServerForwarding(sshd, resolver); sshd.setCommandFactory(new ScpCommandFactory.Builder().withDelegate( command -> new ProcessShellFactory(GenericUtils.split(command, ' ')).create() ).build()); @@ -595,13 +603,13 @@ public class SshServer extends AbstractFactoryManager implements ServerFactoryMa Thread.sleep(Long.MAX_VALUE); } - public static ForwardingFilter setupServerForwarding(SshServer server, Map<String, ?> options) { + public static ForwardingFilter setupServerForwarding(SshServer server, PropertyResolver options) { ForwardingFilter forwardFilter = SshServerConfigFileReader.resolveServerForwarding(options); server.setForwardingFilter(forwardFilter); return forwardFilter; } - public static Object setupServerBanner(ServerFactoryManager server, Map<String, ?> options) { + public static Object setupServerBanner(ServerFactoryManager server, PropertyResolver options) { Object banner = SshServerConfigFileReader.resolveBanner(options); PropertyResolverUtils.updateProperty(server, ServerAuthenticationManager.WELCOME_BANNER, banner); return banner; http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java index c5acd09..381d2a3 100644 --- a/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java +++ b/sshd-core/src/main/java/org/apache/sshd/server/config/SshServerConfigFileReader.java @@ -19,13 +19,16 @@ package org.apache.sshd.server.config; import java.nio.file.Paths; -import java.util.Map; +import org.apache.sshd.common.PropertyResolver; import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.config.SshConfigFileReader; +import org.apache.sshd.common.helpers.AbstractFactoryManager; import org.apache.sshd.common.util.GenericUtils; import org.apache.sshd.common.util.ValidateUtils; import org.apache.sshd.server.ServerAuthenticationManager; +import org.apache.sshd.server.ServerBuilder; +import org.apache.sshd.server.SshServer; import org.apache.sshd.server.forward.AcceptAllForwardingFilter; import org.apache.sshd.server.forward.AgentForwardingFilter; import org.apache.sshd.server.forward.ForwardingFilter; @@ -59,7 +62,13 @@ public final class SshServerConfigFileReader { throw new UnsupportedOperationException("No instance allowed"); } - public static ForwardingFilter resolveServerForwarding(Map<String, ?> options) { + public static <S extends SshServer> S configure(S server, PropertyResolver props, boolean lenient, boolean ignoreUnsupported) { + SshConfigFileReader.configure((AbstractFactoryManager) server, props, lenient, ignoreUnsupported); + SshConfigFileReader.configureKeyExchanges(server, props, lenient, ServerBuilder.DH2KEX, ignoreUnsupported); + return server; + } + + public static ForwardingFilter resolveServerForwarding(PropertyResolver options) { if (GenericUtils.isEmpty(options)) { return AcceptAllForwardingFilter.INSTANCE; } @@ -70,24 +79,24 @@ public final class SshServerConfigFileReader { return ForwardingFilter.asForwardingFilter(agentFilter, x11Filter, tcpFilter); } - public static AgentForwardingFilter resolveAgentForwardingFilter(Map<String, ?> options) { + public static AgentForwardingFilter resolveAgentForwardingFilter(PropertyResolver options) { String value = PropertyResolverUtils.getStringProperty(options, ALLOW_AGENT_FORWARDING_CONFIG_PROP, DEFAULT_AGENT_FORWARDING); return AgentForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value)); } - public static TcpForwardingFilter resolveTcpForwardingFilter(Map<String, ?> options) { + public static TcpForwardingFilter resolveTcpForwardingFilter(PropertyResolver options) { String value = PropertyResolverUtils.getStringProperty(options, ALLOW_TCP_FORWARDING_CONFIG_PROP, DEFAULT_TCP_FORWARDING); TcpForwardingFilter filter = AllowTcpForwardingValue.fromString(value); ValidateUtils.checkNotNull(filter, "Unknown %s value: %s", ALLOW_TCP_FORWARDING_CONFIG_PROP, value); return filter; } - public static X11ForwardingFilter resolveX11ForwardingFilter(Map<String, ?> options) { + public static X11ForwardingFilter resolveX11ForwardingFilter(PropertyResolver options) { String value = PropertyResolverUtils.getStringProperty(options, ALLOW_X11_FORWARDING_CONFIG_PROP, DEFAULT_X11_FORWARDING); return X11ForwardingFilter.of(SshConfigFileReader.parseBooleanValue(value)); } - public static Object resolveBanner(Map<String, ?> options) { + public static Object resolveBanner(PropertyResolver options) { String bannerOption = PropertyResolverUtils.getString(options, BANNER_CONFIG_PROP); if (GenericUtils.isEmpty(bannerOption)) { bannerOption = PropertyResolverUtils.getStringProperty(options, VISUAL_HOST_KEY, DEFAULT_VISUAL_HOST_KEY); http://git-wip-us.apache.org/repos/asf/mina-sshd/blob/a65c2f27/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java ---------------------------------------------------------------------- diff --git a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java index b806f9e..4477da1 100644 --- a/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java +++ b/sshd-core/src/test/java/org/apache/sshd/common/config/SshConfigFileReaderTest.java @@ -32,6 +32,7 @@ import org.apache.sshd.common.Closeable; import org.apache.sshd.common.FactoryManager; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.NamedResource; +import org.apache.sshd.common.PropertyResolverUtils; import org.apache.sshd.common.cipher.BuiltinCiphers; import org.apache.sshd.common.cipher.Cipher; import org.apache.sshd.common.compression.BuiltinCompressions; @@ -80,7 +81,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport { public void testParseCiphersList() { List<? extends NamedResource> expected = BaseBuilder.DEFAULT_CIPHERS_PREFERENCE; Properties props = initNamedResourceProperties(SshConfigFileReader.CIPHERS_CONFIG_PROP, expected); - BuiltinCiphers.ParseResult result = SshConfigFileReader.getCiphers(props); + BuiltinCiphers.ParseResult result = SshConfigFileReader.getCiphers(PropertyResolverUtils.toPropertyResolver(props)); testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories()); } @@ -88,7 +89,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport { public void testParseMacsList() { List<? extends NamedResource> expected = BaseBuilder.DEFAULT_MAC_PREFERENCE; Properties props = initNamedResourceProperties(SshConfigFileReader.MACS_CONFIG_PROP, expected); - BuiltinMacs.ParseResult result = SshConfigFileReader.getMacs(props); + BuiltinMacs.ParseResult result = SshConfigFileReader.getMacs(PropertyResolverUtils.toPropertyResolver(props)); testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories()); } @@ -96,7 +97,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport { public void testParseSignaturesList() { List<? extends NamedResource> expected = BaseBuilder.DEFAULT_SIGNATURE_PREFERENCE; Properties props = initNamedResourceProperties(SshConfigFileReader.HOST_KEY_ALGORITHMS_CONFIG_PROP, expected); - BuiltinSignatures.ParseResult result = SshConfigFileReader.getSignatures(props); + BuiltinSignatures.ParseResult result = SshConfigFileReader.getSignatures(PropertyResolverUtils.toPropertyResolver(props)); testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories()); } @@ -104,7 +105,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport { public void testParseKexFactoriesList() { List<? extends NamedResource> expected = BaseBuilder.DEFAULT_KEX_PREFERENCE; Properties props = initNamedResourceProperties(SshConfigFileReader.KEX_ALGORITHMS_CONFIG_PROP, expected); - BuiltinDHFactories.ParseResult result = SshConfigFileReader.getKexFactories(props); + BuiltinDHFactories.ParseResult result = SshConfigFileReader.getKexFactories(PropertyResolverUtils.toPropertyResolver(props)); testParsedFactoriesList(expected, result.getParsedFactories(), result.getUnsupportedFactories()); } @@ -114,7 +115,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport { for (CompressionConfigValue expected : CompressionConfigValue.VALUES) { props.setProperty(SshConfigFileReader.COMPRESSION_PROP, expected.name().toLowerCase()); - NamedResource actual = SshConfigFileReader.getCompression(props); + NamedResource actual = SshConfigFileReader.getCompression(PropertyResolverUtils.toPropertyResolver(props)); assertNotNull("No match for " + expected.name(), actual); assertEquals(expected.name(), expected.getName(), actual.getName()); } @@ -130,7 +131,7 @@ public class SshConfigFileReaderTest extends BaseTestSupport { } }; // must be lenient since we do not cover the full default spectrum - AbstractFactoryManager actual = SshConfigFileReader.configure(expected, props, true, true); + AbstractFactoryManager actual = SshConfigFileReader.configure(expected, PropertyResolverUtils.toPropertyResolver(props), true, true); assertSame("Mismatched configured result", expected, actual); validateAbstractFactoryManagerConfiguration(expected, props, true); }