View Javadoc
1   /**
2    * Copyright 2010-2013 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.util.channel.model;
17  
18  import static com.google.common.base.Optional.absent;
19  import static org.kuali.common.util.base.Precondition.checkNotBlank;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Properties;
25  
26  import org.kuali.common.util.Assert;
27  import org.kuali.common.util.CollectionUtils;
28  import org.kuali.common.util.LocationUtils;
29  import org.kuali.common.util.channel.util.SSHUtils;
30  import org.kuali.common.util.enc.EncUtils;
31  import org.kuali.common.util.enc.EncryptionService;
32  import org.kuali.common.util.nullify.NullUtils;
33  import org.kuali.common.util.property.ImmutableProperties;
34  import org.kuali.common.util.spring.SpringUtils;
35  import org.kuali.common.util.spring.env.EnvUtils;
36  import org.kuali.common.util.spring.env.EnvironmentService;
37  
38  import com.google.common.base.Charsets;
39  import com.google.common.base.Optional;
40  import com.google.common.collect.ImmutableList;
41  
42  /**
43   * @deprecated
44   */
45  @Deprecated
46  public final class ChannelContext {
47  
48  	private final Optional<String> username;
49  	private final String hostname;
50  	private final List<String> privateKeys;
51  	private final int port;
52  	private final String encoding;
53  	private final boolean strictHostKeyChecking;
54  	private final boolean requestPseudoTerminal;
55  	private final Optional<Integer> connectTimeout;
56  	private final Properties options;
57  	private final File knownHosts;
58  	private final File config;
59  	private final boolean useConfigFile;
60  	private final boolean useKnownHosts;
61  	private final boolean includeDefaultPrivateKeyLocations;
62  	private final int waitForClosedSleepMillis;
63  	private final List<File> privateKeyFiles;
64  	private final boolean echo;
65  	private final boolean debug;
66  
67  	public static class Builder {
68  
69  		// Required
70  		private final String hostname;
71  
72  		// Optional
73  		private Optional<String> username = absent();
74  		private int port = 22;
75  		private Optional<Integer> connectTimeout = absent();
76  		private String encoding = Charsets.UTF_8.name();
77  		private Properties options = ImmutableProperties.of();
78  		private boolean strictHostKeyChecking = false;
79  		private boolean requestPseudoTerminal = false;
80  		private File knownHosts = SSHUtils.DEFAULT_KNOWN_HOSTS;
81  		private boolean useKnownHosts = false;
82  		private File config = SSHUtils.DEFAULT_CONFIG_FILE;
83  		private boolean useConfigFile = false;
84  		private boolean includeDefaultPrivateKeyLocations = false;
85  		private int waitForClosedSleepMillis = 10; // number of milliseconds to sleep when looping to see if an exec'd command has finished
86  		private List<File> privateKeyFiles = ImmutableList.of();
87  		private List<String> privateKeys = ImmutableList.of();
88  		private boolean echo = true;
89  		private boolean debug = false;
90  
91  		// Used only by the builder
92  		private final Optional<EnvironmentService> env;
93  		private final Optional<EncryptionService> enc;
94  		private static final String HOSTNAME_KEY = "ssh.hostname";
95  		private static final String USERNAME_KEY = "ssh.username";
96  		private static final String REQUEST_PSEUDO_TERMINAL_KEY = "ssh.requestPseudoTerminal";
97  		private static final String PRIVATE_KEYS_KEY = "ssh.privateKeys";
98  		private static final String ECHO_KEY = "ssh.echo";
99  		private static final String PORT_KEY = "ssh.port";
100 		private static final String ENCODING_KEY = "ssh.encoding";
101 		private static final String CONNECT_TIMEOUT_KEY = "ssh.connectTimeout";
102 
103 		/**
104 		 * 
105 		 */
106 		public Builder(String hostname) {
107 			this(EnvUtils.ABSENT, EncUtils.ABSENT, hostname);
108 		}
109 
110 		/**
111 		 * Override using <code>ssh.hostname</code> (if present)
112 		 */
113 		public Builder(EnvironmentService env, String hostname) {
114 			this(Optional.of(env), EncUtils.ABSENT, hostname);
115 		}
116 
117 		/**
118 		 * Override using <code>ssh.hostname</code> (if present)
119 		 */
120 		public Builder(EnvironmentService env, EncryptionService enc, String hostname) {
121 			this(Optional.of(env), Optional.of(enc), hostname);
122 		}
123 
124 		/**
125 		 * Override using <code>ssh.hostname</code> (if present)
126 		 */
127 		private Builder(Optional<EnvironmentService> env, Optional<EncryptionService> enc, String hostname) {
128 			if (env.isPresent()) {
129 				this.hostname = env.get().getString(HOSTNAME_KEY, hostname);
130 			} else {
131 				this.hostname = hostname;
132 			}
133 			this.env = env;
134 			this.enc = enc;
135 		}
136 
137 		public Builder requestPseudoTerminal(boolean requestPseudoTerminal) {
138 			this.requestPseudoTerminal = requestPseudoTerminal;
139 			return this;
140 		}
141 
142 		public Builder debug(boolean debug) {
143 			this.debug = debug;
144 			return this;
145 		}
146 
147 		public Builder echo(boolean echo) {
148 			this.echo = echo;
149 			return this;
150 		}
151 
152 		public Builder username(String username) {
153 			this.username = NullUtils.toAbsent(username);
154 			return this;
155 		}
156 
157 		public Builder port(int port) {
158 			this.port = port;
159 			return this;
160 		}
161 
162 		public Builder encoding(String encoding) {
163 			this.encoding = encoding;
164 			return this;
165 		}
166 
167 		public Builder connectTimeout(int connectTimeout) {
168 			this.connectTimeout = Optional.of(connectTimeout);
169 			return this;
170 		}
171 
172 		public Builder options(Properties options) {
173 			this.options = options;
174 			return this;
175 		}
176 
177 		public Builder knownHosts(File knownHosts) {
178 			this.knownHosts = knownHosts;
179 			return this;
180 		}
181 
182 		public Builder useKnownHosts(boolean useKnownHosts) {
183 			this.useKnownHosts = useKnownHosts;
184 			return this;
185 		}
186 
187 		public Builder config(File config) {
188 			this.config = config;
189 			return this;
190 		}
191 
192 		public Builder useConfigFile(boolean useConfigFile) {
193 			this.useConfigFile = useConfigFile;
194 			return this;
195 		}
196 
197 		public Builder includeDefaultPrivateKeyLocations(boolean includeDefaultPrivateKeyLocations) {
198 			this.includeDefaultPrivateKeyLocations = includeDefaultPrivateKeyLocations;
199 			return this;
200 		}
201 
202 		public Builder waitForClosedSleepMillis(int waitForClosedSleepMillis) {
203 			this.waitForClosedSleepMillis = waitForClosedSleepMillis;
204 			return this;
205 		}
206 
207 		public Builder privateKeyFiles(List<File> privateKeyFiles) {
208 			this.privateKeyFiles = privateKeyFiles;
209 			return this;
210 		}
211 
212 		public Builder privateKey(String privateKey) {
213 			Optional<String> trimmed = NullUtils.toAbsent(privateKey);
214 			if (trimmed.isPresent()) {
215 				return privateKeys(ImmutableList.of(trimmed.get()));
216 			} else {
217 				return privateKeys(ImmutableList.<String> of());
218 			}
219 		}
220 
221 		public Builder privateKeys(List<String> privateKeys) {
222 			this.privateKeys = privateKeys;
223 			return this;
224 		}
225 
226 		/**
227 		 * Override provided values with values from the environment
228 		 */
229 		private void override() {
230 			if (env.isPresent()) {
231 				username(SpringUtils.getString(env.get(), USERNAME_KEY, username).orNull());
232 				requestPseudoTerminal(env.get().getBoolean(REQUEST_PSEUDO_TERMINAL_KEY, requestPseudoTerminal));
233 				privateKeys(SpringUtils.getStrings(env.get(), PRIVATE_KEYS_KEY, privateKeys));
234 				echo(env.get().getBoolean(ECHO_KEY, echo));
235 				port(env.get().getInteger(PORT_KEY, port));
236 				encoding(env.get().getString(ENCODING_KEY, encoding));
237 				Optional<Integer> connectTimeout = SpringUtils.getProperty(env, CONNECT_TIMEOUT_KEY, Integer.class, this.connectTimeout);
238 				if (connectTimeout.isPresent()) {
239 					connectTimeout(connectTimeout.get());
240 				}
241 			}
242 		}
243 
244 		private void finish() {
245 			override();
246 			privateKeys(EncUtils.decrypt(enc, privateKeys));
247 			this.privateKeyFiles = ImmutableList.copyOf(getUniquePrivateKeyFiles(privateKeyFiles, useConfigFile, config, includeDefaultPrivateKeyLocations));
248 			this.privateKeys = ImmutableList.copyOf(privateKeys);
249 			this.options = ImmutableProperties.copyOf(getSessionProperties(options, strictHostKeyChecking));
250 		}
251 
252 		private static void validate(ChannelContext ctx) {
253 			checkNotBlank(ctx.hostname, "hostname");
254 			checkNotBlank(ctx.encoding, "encoding");
255 			Assert.noNulls(ctx.getUsername(), ctx.getConnectTimeout(), ctx.getOptions(), ctx.getKnownHosts(), ctx.getConfig(), ctx.getPrivateKeyFiles(), ctx.getPrivateKeys());
256 			Assert.isPort(ctx.getPort());
257 			Assert.positive(ctx.getWaitForClosedSleepMillis());
258 			Assert.notEncrypted(ctx.getPrivateKeys());
259 			if (ctx.isUseConfigFile()) {
260 				Assert.exists(ctx.getConfig());
261 				Assert.isTrue(ctx.getConfig().canRead(), "[" + ctx.getConfig() + "] exists but is not readable");
262 			}
263 			if (ctx.getConnectTimeout().isPresent()) {
264 				Assert.positive(ctx.getConnectTimeout().get());
265 			}
266 		}
267 
268 		public ChannelContext build() {
269 			finish();
270 			ChannelContext ctx = new ChannelContext(this);
271 			validate(ctx);
272 			return ctx;
273 		}
274 
275 		private List<File> getUniquePrivateKeyFiles(List<File> privateKeyFiles, boolean useConfigFile, File config, boolean includeDefaultPrivateKeyLocations) {
276 			List<String> paths = new ArrayList<String>();
277 			for (File privateKeyFile : privateKeyFiles) {
278 				paths.add(LocationUtils.getCanonicalPath(privateKeyFile));
279 			}
280 			if (useConfigFile) {
281 				for (String path : SSHUtils.getFilenames(config)) {
282 					paths.add(path);
283 				}
284 			}
285 			if (includeDefaultPrivateKeyLocations) {
286 				for (String path : SSHUtils.PRIVATE_KEY_DEFAULTS) {
287 					paths.add(path);
288 				}
289 			}
290 			List<String> uniquePaths = CollectionUtils.getUniqueStrings(paths);
291 			return SSHUtils.getExistingAndReadable(uniquePaths);
292 		}
293 
294 		private Properties getSessionProperties(Properties options, boolean strictHostKeyChecking) {
295 			Properties properties = new Properties();
296 			properties.putAll(options);
297 			if (!strictHostKeyChecking) {
298 				properties.setProperty(SSHUtils.STRICT_HOST_KEY_CHECKING, SSHUtils.NO);
299 			}
300 			return properties;
301 		}
302 	}
303 
304 	private ChannelContext(Builder builder) {
305 		this.username = builder.username;
306 		this.hostname = builder.hostname;
307 		this.port = builder.port;
308 		this.encoding = builder.encoding;
309 		this.connectTimeout = builder.connectTimeout;
310 		this.options = builder.options;
311 		this.strictHostKeyChecking = builder.strictHostKeyChecking;
312 		this.requestPseudoTerminal = builder.requestPseudoTerminal;
313 		this.knownHosts = builder.knownHosts;
314 		this.config = builder.config;
315 		this.useConfigFile = builder.useConfigFile;
316 		this.includeDefaultPrivateKeyLocations = builder.includeDefaultPrivateKeyLocations;
317 		this.waitForClosedSleepMillis = builder.waitForClosedSleepMillis;
318 		this.privateKeyFiles = builder.privateKeyFiles;
319 		this.privateKeys = builder.privateKeys;
320 		this.useKnownHosts = builder.useKnownHosts;
321 		this.echo = builder.echo;
322 		this.debug = builder.debug;
323 	}
324 
325 	public Optional<String> getUsername() {
326 		return username;
327 	}
328 
329 	public String getHostname() {
330 		return hostname;
331 	}
332 
333 	public int getPort() {
334 		return port;
335 	}
336 
337 	public Optional<Integer> getConnectTimeout() {
338 		return connectTimeout;
339 	}
340 
341 	public String getEncoding() {
342 		return encoding;
343 	}
344 
345 	public Properties getOptions() {
346 		return options;
347 	}
348 
349 	public boolean isStrictHostKeyChecking() {
350 		return strictHostKeyChecking;
351 	}
352 
353 	public boolean isRequestPseudoTerminal() {
354 		return requestPseudoTerminal;
355 	}
356 
357 	public File getKnownHosts() {
358 		return knownHosts;
359 	}
360 
361 	public File getConfig() {
362 		return config;
363 	}
364 
365 	public boolean isUseConfigFile() {
366 		return useConfigFile;
367 	}
368 
369 	public boolean isUseKnownHosts() {
370 		return useKnownHosts;
371 	}
372 
373 	public boolean isIncludeDefaultPrivateKeyLocations() {
374 		return includeDefaultPrivateKeyLocations;
375 	}
376 
377 	public int getWaitForClosedSleepMillis() {
378 		return waitForClosedSleepMillis;
379 	}
380 
381 	public List<File> getPrivateKeyFiles() {
382 		return privateKeyFiles;
383 	}
384 
385 	public List<String> getPrivateKeys() {
386 		return privateKeys;
387 	}
388 
389 	public boolean isEcho() {
390 		return echo;
391 	}
392 
393 	public boolean isDebug() {
394 		return debug;
395 	}
396 
397 }