001/** 002 * Copyright 2010-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.util.channel.model; 017 018import static com.google.common.base.Optional.absent; 019import static org.kuali.common.util.base.Precondition.checkNotBlank; 020 021import java.io.File; 022import java.util.ArrayList; 023import java.util.List; 024import java.util.Properties; 025 026import org.kuali.common.util.Assert; 027import org.kuali.common.util.CollectionUtils; 028import org.kuali.common.util.LocationUtils; 029import org.kuali.common.util.channel.util.SSHUtils; 030import org.kuali.common.util.enc.EncUtils; 031import org.kuali.common.util.enc.EncryptionService; 032import org.kuali.common.util.nullify.NullUtils; 033import org.kuali.common.util.property.ImmutableProperties; 034import org.kuali.common.util.spring.SpringUtils; 035import org.kuali.common.util.spring.env.EnvUtils; 036import org.kuali.common.util.spring.env.EnvironmentService; 037 038import com.google.common.base.Charsets; 039import com.google.common.base.Optional; 040import com.google.common.collect.ImmutableList; 041 042/** 043 * @deprecated 044 */ 045@Deprecated 046public final class ChannelContext { 047 048 private final Optional<String> username; 049 private final String hostname; 050 private final List<String> privateKeys; 051 private final int port; 052 private final String encoding; 053 private final boolean strictHostKeyChecking; 054 private final boolean requestPseudoTerminal; 055 private final Optional<Integer> connectTimeout; 056 private final Properties options; 057 private final File knownHosts; 058 private final File config; 059 private final boolean useConfigFile; 060 private final boolean useKnownHosts; 061 private final boolean includeDefaultPrivateKeyLocations; 062 private final int waitForClosedSleepMillis; 063 private final List<File> privateKeyFiles; 064 private final boolean echo; 065 private final boolean debug; 066 067 public static Builder builder(String hostname) { 068 return new Builder(hostname); 069 } 070 071 public static class Builder { 072 073 // Required 074 private final String hostname; 075 076 // Optional 077 private Optional<String> username = absent(); 078 private int port = 22; 079 private Optional<Integer> connectTimeout = absent(); 080 private String encoding = Charsets.UTF_8.name(); 081 private Properties options = ImmutableProperties.of(); 082 private boolean strictHostKeyChecking = false; 083 private boolean requestPseudoTerminal = false; 084 private File knownHosts = SSHUtils.DEFAULT_KNOWN_HOSTS; 085 private boolean useKnownHosts = false; 086 private File config = SSHUtils.DEFAULT_CONFIG_FILE; 087 private boolean useConfigFile = false; 088 private boolean includeDefaultPrivateKeyLocations = false; 089 private int waitForClosedSleepMillis = 10; // number of milliseconds to sleep when looping to see if an exec'd command has finished 090 private List<File> privateKeyFiles = ImmutableList.of(); 091 private List<String> privateKeys = ImmutableList.of(); 092 private boolean echo = true; 093 private boolean debug = false; 094 095 // Used only by the builder 096 private final Optional<EnvironmentService> env; 097 private final Optional<EncryptionService> enc; 098 private static final String HOSTNAME_KEY = "ssh.hostname"; 099 private static final String USERNAME_KEY = "ssh.username"; 100 private static final String REQUEST_PSEUDO_TERMINAL_KEY = "ssh.requestPseudoTerminal"; 101 private static final String PRIVATE_KEYS_KEY = "ssh.privateKeys"; 102 private static final String ECHO_KEY = "ssh.echo"; 103 private static final String PORT_KEY = "ssh.port"; 104 private static final String ENCODING_KEY = "ssh.encoding"; 105 private static final String CONNECT_TIMEOUT_KEY = "ssh.connectTimeout"; 106 107 /** 108 * 109 */ 110 public Builder(String hostname) { 111 this(EnvUtils.ABSENT, EncUtils.ABSENT, hostname); 112 } 113 114 /** 115 * Override using <code>ssh.hostname</code> (if present) 116 */ 117 public Builder(EnvironmentService env, String hostname) { 118 this(Optional.of(env), EncUtils.ABSENT, hostname); 119 } 120 121 /** 122 * Override using <code>ssh.hostname</code> (if present) 123 */ 124 public Builder(EnvironmentService env, EncryptionService enc, String hostname) { 125 this(Optional.of(env), Optional.of(enc), hostname); 126 } 127 128 /** 129 * Override using <code>ssh.hostname</code> (if present) 130 */ 131 private Builder(Optional<EnvironmentService> env, Optional<EncryptionService> enc, String hostname) { 132 if (env.isPresent()) { 133 this.hostname = env.get().getString(HOSTNAME_KEY, hostname); 134 } else { 135 this.hostname = hostname; 136 } 137 this.env = env; 138 this.enc = enc; 139 } 140 141 public Builder requestPseudoTerminal(boolean requestPseudoTerminal) { 142 this.requestPseudoTerminal = requestPseudoTerminal; 143 return this; 144 } 145 146 public Builder debug(boolean debug) { 147 this.debug = debug; 148 return this; 149 } 150 151 public Builder echo(boolean echo) { 152 this.echo = echo; 153 return this; 154 } 155 156 public Builder username(String username) { 157 this.username = NullUtils.toAbsent(username); 158 return this; 159 } 160 161 public Builder port(int port) { 162 this.port = port; 163 return this; 164 } 165 166 public Builder encoding(String encoding) { 167 this.encoding = encoding; 168 return this; 169 } 170 171 public Builder connectTimeout(int connectTimeout) { 172 this.connectTimeout = Optional.of(connectTimeout); 173 return this; 174 } 175 176 public Builder options(Properties options) { 177 this.options = options; 178 return this; 179 } 180 181 public Builder knownHosts(File knownHosts) { 182 this.knownHosts = knownHosts; 183 return this; 184 } 185 186 public Builder useKnownHosts(boolean useKnownHosts) { 187 this.useKnownHosts = useKnownHosts; 188 return this; 189 } 190 191 public Builder config(File config) { 192 this.config = config; 193 return this; 194 } 195 196 public Builder useConfigFile(boolean useConfigFile) { 197 this.useConfigFile = useConfigFile; 198 return this; 199 } 200 201 public Builder includeDefaultPrivateKeyLocations(boolean includeDefaultPrivateKeyLocations) { 202 this.includeDefaultPrivateKeyLocations = includeDefaultPrivateKeyLocations; 203 return this; 204 } 205 206 public Builder waitForClosedSleepMillis(int waitForClosedSleepMillis) { 207 this.waitForClosedSleepMillis = waitForClosedSleepMillis; 208 return this; 209 } 210 211 public Builder privateKeyFiles(List<File> privateKeyFiles) { 212 this.privateKeyFiles = privateKeyFiles; 213 return this; 214 } 215 216 public Builder privateKey(String privateKey) { 217 Optional<String> trimmed = NullUtils.toAbsent(privateKey); 218 if (trimmed.isPresent()) { 219 return privateKeys(ImmutableList.of(trimmed.get())); 220 } else { 221 return privateKeys(ImmutableList.<String> of()); 222 } 223 } 224 225 public Builder privateKeys(List<String> privateKeys) { 226 this.privateKeys = privateKeys; 227 return this; 228 } 229 230 /** 231 * Override provided values with values from the environment 232 */ 233 private void override() { 234 if (env.isPresent()) { 235 username(SpringUtils.getString(env.get(), USERNAME_KEY, username).orNull()); 236 requestPseudoTerminal(env.get().getBoolean(REQUEST_PSEUDO_TERMINAL_KEY, requestPseudoTerminal)); 237 privateKeys(SpringUtils.getStrings(env.get(), PRIVATE_KEYS_KEY, privateKeys)); 238 echo(env.get().getBoolean(ECHO_KEY, echo)); 239 port(env.get().getInteger(PORT_KEY, port)); 240 encoding(env.get().getString(ENCODING_KEY, encoding)); 241 Optional<Integer> connectTimeout = SpringUtils.getProperty(env, CONNECT_TIMEOUT_KEY, Integer.class, this.connectTimeout); 242 if (connectTimeout.isPresent()) { 243 connectTimeout(connectTimeout.get()); 244 } 245 } 246 } 247 248 private void finish() { 249 override(); 250 privateKeys(EncUtils.decrypt(enc, privateKeys)); 251 this.privateKeyFiles = ImmutableList.copyOf(getUniquePrivateKeyFiles(privateKeyFiles, useConfigFile, config, includeDefaultPrivateKeyLocations)); 252 this.privateKeys = ImmutableList.copyOf(privateKeys); 253 this.options = ImmutableProperties.copyOf(getSessionProperties(options, strictHostKeyChecking)); 254 } 255 256 private static void validate(ChannelContext ctx) { 257 checkNotBlank(ctx.hostname, "hostname"); 258 checkNotBlank(ctx.encoding, "encoding"); 259 Assert.noNulls(ctx.getUsername(), ctx.getConnectTimeout(), ctx.getOptions(), ctx.getKnownHosts(), ctx.getConfig(), ctx.getPrivateKeyFiles(), ctx.getPrivateKeys()); 260 Assert.isPort(ctx.getPort()); 261 Assert.positive(ctx.getWaitForClosedSleepMillis()); 262 Assert.notEncrypted(ctx.getPrivateKeys()); 263 if (ctx.isUseConfigFile()) { 264 Assert.exists(ctx.getConfig()); 265 Assert.isTrue(ctx.getConfig().canRead(), "[" + ctx.getConfig() + "] exists but is not readable"); 266 } 267 if (ctx.getConnectTimeout().isPresent()) { 268 Assert.positive(ctx.getConnectTimeout().get()); 269 } 270 } 271 272 public ChannelContext build() { 273 finish(); 274 ChannelContext ctx = new ChannelContext(this); 275 validate(ctx); 276 return ctx; 277 } 278 279 private List<File> getUniquePrivateKeyFiles(List<File> privateKeyFiles, boolean useConfigFile, File config, boolean includeDefaultPrivateKeyLocations) { 280 List<String> paths = new ArrayList<String>(); 281 for (File privateKeyFile : privateKeyFiles) { 282 paths.add(LocationUtils.getCanonicalPath(privateKeyFile)); 283 } 284 if (useConfigFile) { 285 for (String path : SSHUtils.getFilenames(config)) { 286 paths.add(path); 287 } 288 } 289 if (includeDefaultPrivateKeyLocations) { 290 for (String path : SSHUtils.PRIVATE_KEY_DEFAULTS) { 291 paths.add(path); 292 } 293 } 294 List<String> uniquePaths = CollectionUtils.getUniqueStrings(paths); 295 return SSHUtils.getExistingAndReadable(uniquePaths); 296 } 297 298 private Properties getSessionProperties(Properties options, boolean strictHostKeyChecking) { 299 Properties properties = new Properties(); 300 properties.putAll(options); 301 if (!strictHostKeyChecking) { 302 properties.setProperty(SSHUtils.STRICT_HOST_KEY_CHECKING, SSHUtils.NO); 303 } 304 return properties; 305 } 306 } 307 308 private ChannelContext(Builder builder) { 309 this.username = builder.username; 310 this.hostname = builder.hostname; 311 this.port = builder.port; 312 this.encoding = builder.encoding; 313 this.connectTimeout = builder.connectTimeout; 314 this.options = builder.options; 315 this.strictHostKeyChecking = builder.strictHostKeyChecking; 316 this.requestPseudoTerminal = builder.requestPseudoTerminal; 317 this.knownHosts = builder.knownHosts; 318 this.config = builder.config; 319 this.useConfigFile = builder.useConfigFile; 320 this.includeDefaultPrivateKeyLocations = builder.includeDefaultPrivateKeyLocations; 321 this.waitForClosedSleepMillis = builder.waitForClosedSleepMillis; 322 this.privateKeyFiles = builder.privateKeyFiles; 323 this.privateKeys = builder.privateKeys; 324 this.useKnownHosts = builder.useKnownHosts; 325 this.echo = builder.echo; 326 this.debug = builder.debug; 327 } 328 329 public Optional<String> getUsername() { 330 return username; 331 } 332 333 public String getHostname() { 334 return hostname; 335 } 336 337 public int getPort() { 338 return port; 339 } 340 341 public Optional<Integer> getConnectTimeout() { 342 return connectTimeout; 343 } 344 345 public String getEncoding() { 346 return encoding; 347 } 348 349 public Properties getOptions() { 350 return options; 351 } 352 353 public boolean isStrictHostKeyChecking() { 354 return strictHostKeyChecking; 355 } 356 357 public boolean isRequestPseudoTerminal() { 358 return requestPseudoTerminal; 359 } 360 361 public File getKnownHosts() { 362 return knownHosts; 363 } 364 365 public File getConfig() { 366 return config; 367 } 368 369 public boolean isUseConfigFile() { 370 return useConfigFile; 371 } 372 373 public boolean isUseKnownHosts() { 374 return useKnownHosts; 375 } 376 377 public boolean isIncludeDefaultPrivateKeyLocations() { 378 return includeDefaultPrivateKeyLocations; 379 } 380 381 public int getWaitForClosedSleepMillis() { 382 return waitForClosedSleepMillis; 383 } 384 385 public List<File> getPrivateKeyFiles() { 386 return privateKeyFiles; 387 } 388 389 public List<String> getPrivateKeys() { 390 return privateKeys; 391 } 392 393 public boolean isEcho() { 394 return echo; 395 } 396 397 public boolean isDebug() { 398 return debug; 399 } 400 401}