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}