2 * Copyright © 2019 FRINX s.r.o. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.restconf.websocket.client;
11 import com.google.common.base.Preconditions;
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.io.PrintWriter;
15 import java.io.StringWriter;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.Optional;
19 import net.sourceforge.argparse4j.ArgumentParsers;
20 import net.sourceforge.argparse4j.annotation.Arg;
21 import net.sourceforge.argparse4j.helper.HelpScreenException;
22 import net.sourceforge.argparse4j.inf.ArgumentParser;
23 import net.sourceforge.argparse4j.inf.ArgumentParserException;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
28 * Holder of the parsed user-input application arguments.
30 final class ApplicationSettings {
33 * Credentials used for basic authentication - grouping of username and password.
35 static final class Credentials {
36 final String userName;
37 final String password;
39 private Credentials(final String userName, final String password) {
40 this.userName = userName;
41 this.password = password;
44 @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
45 justification = "https://github.com/spotbugs/spotbugs/issues/811")
46 private static Credentials extractCredentials(final String basicAuthentication) {
47 final String[] credentials = basicAuthentication.split(":");
48 Preconditions.checkArgument(credentials.length == 2, "Both username and password must be specified in the "
49 + "format [username]:[password] for basic authentication.");
50 final String userName = credentials[0].trim();
51 final String password = credentials[1].trim();
52 return new Credentials(userName, password);
56 private static final Logger LOG = LoggerFactory.getLogger(ApplicationSettings.class);
57 private static final ArgumentParser PARSER = ArgumentParsers.newFor("web-socket client test-tool").build();
60 PARSER.addArgument("-l")
66 .help("Logging level threshold used throughout the whole web-socket client.");
67 PARSER.addArgument("-s")
71 .help("Web-socket stream paths with ws or wss schemas.")
74 PARSER.addArgument("-pi")
76 .help("Interval in milliseconds between sending of ping web-socket frames to server. "
77 + "Value of 0 disables ping process.")
81 PARSER.addArgument("-pm")
83 .help("Explicitly set ping message.")
87 PARSER.addArgument("-t")
89 .help("Explicitly set size of thread-pool used for holding of web-socket handlers and ping processes.")
93 PARSER.addArgument("-r")
95 .help("Allowed TLS/SSL session regeneration.")
99 PARSER.addArgument("-kpath")
100 .dest("keystorePath")
101 .help("Path to the certificates key-store file.")
104 PARSER.addArgument("-kpass")
105 .dest("keystorePassword")
106 .help("Password used for unlocking of the certificates keystore.")
109 PARSER.addArgument("-tpath")
110 .dest("truststorePath")
111 .help("Path to the certificates trust-store file.")
114 PARSER.addArgument("-tpass")
115 .dest("truststorePassword")
116 .help("Password used for unlocking of the certificates truststore.")
119 PARSER.addArgument("-ta")
121 .help("All incoming certificates are trusted when both truststore and keystore are not specified.")
124 .type(Boolean.class);
125 PARSER.addArgument("-ip")
126 .dest("includedProtocols")
128 .help("Explicitly specified list of permitted versions of web-security protocols.")
130 .setDefault("TLSv1.2", "TLSv1.3")
132 PARSER.addArgument("-ep")
133 .dest("excludedProtocols")
135 .help("Explicitly specified list of denied versions of web-security protocols (denied protocols have "
136 + "the highest priority).")
138 .setDefault("TLSv1", "TLSv1.1", "SSL", "SSLv2", "SSLv2Hello", "SSLv3")
140 PARSER.addArgument("-ic")
141 .dest("includedCipherSuites")
143 .help("Explicitly specified list of permitted cipher suites.")
145 .setDefault("TLS_ECDHE.*", "TLS_DHE_RSA.*")
147 PARSER.addArgument("-ec")
148 .dest("excludedCipherSuites")
150 .help("Explicitly specified list of denied cipher suites (denied ciphers have the highest priority).")
152 .setDefault(".*MD5.*", ".*RC4.*", ".*DSS.*", ".*NULL.*", ".*DES.*")
154 PARSER.addArgument("-b")
155 .dest("basicAuthentication")
156 .help("[username:password] used with basic authentication that can be required on upgrade-request.")
157 .metavar("[USERNAME]:[PASSWORD]")
161 @Arg(dest = "loggingLevel")
162 private String loggingLevel;
163 @Arg(dest = "streams")
164 private List<String> streams;
165 @Arg(dest = "pingInterval")
166 private int pingInterval;
167 @Arg(dest = "pingMessage")
168 private String pingMessage;
169 @Arg(dest = "threads")
170 private int threadPoolSize;
171 @Arg(dest = "regeneration")
172 private boolean regenerationAllowed;
173 @Arg(dest = "keystorePath")
174 private File keystorePath;
175 @Arg(dest = "keystorePassword")
176 private String keystorePassword;
177 @Arg(dest = "truststorePath")
178 private File truststorePath;
179 @Arg(dest = "truststorePassword")
180 private String truststorePassword;
181 @Arg(dest = "trustAll")
182 private boolean trustAll;
183 @Arg(dest = "includedProtocols")
184 private List<String> includedProtocols;
185 @Arg(dest = "excludedProtocols")
186 private List<String> excludedProtocols;
187 @Arg(dest = "includedCipherSuites")
188 private List<String> includedCipherSuites;
189 @Arg(dest = "excludedCipherSuites")
190 private List<String> excludedCipherSuites;
191 @Arg(dest = "basicAuthentication")
192 private String basicAuthentication;
194 private Credentials credentials;
196 private ApplicationSettings() {
200 * Creation of application settings object using input command-line arguments (factory method).
202 * @param arguments Raw program arguments.
203 * @return Parsed arguments wrapped in {@link Optional} or {@link Optional#empty()} if only help is going
206 static Optional<ApplicationSettings> parseApplicationSettings(final String[] arguments) {
207 final ApplicationSettings applicationSettings = new ApplicationSettings();
209 PARSER.parseArgs(arguments, applicationSettings);
210 applicationSettings.verifyParsedArguments();
211 if (applicationSettings.basicAuthentication == null) {
212 applicationSettings.credentials = null;
214 applicationSettings.credentials = Credentials.extractCredentials(
215 applicationSettings.basicAuthentication);
217 } catch (final ArgumentParserException | IllegalArgumentException e) {
218 if (e instanceof HelpScreenException) {
219 return Optional.empty();
221 final StringWriter helpWriter = new StringWriter();
222 final PrintWriter helpPrintWriter = new PrintWriter(helpWriter);
223 PARSER.printHelp(helpPrintWriter);
224 LOG.error("Cannot parse input arguments {}.", arguments, e);
225 LOG.info("Help: {}", helpWriter.toString());
226 throw new IllegalArgumentException("Cannot parse input arguments", e);
229 LOG.info("Application settings {} have been parsed successfully.", (Object) arguments);
230 return Optional.of(applicationSettings);
233 private void verifyParsedArguments() {
234 Preconditions.checkArgument(pingInterval >= 0, "Ping interval must be set to value higher than 0 (enabled) or "
235 + "to 0 (disabled).");
236 Preconditions.checkArgument(threadPoolSize > 0, "Thread pool must have capacity of at least 1 thread.");
237 Preconditions.checkArgument((keystorePath == null && keystorePassword == null) || (keystorePath != null
238 && keystorePassword != null), "Both keystore path and keystore password must be configured at once.");
239 Preconditions.checkArgument((truststorePath == null && truststorePassword == null) || (truststorePath != null
240 && truststorePassword != null), "Both truststore path and truststore password must be configured");
243 String getLoggingLevel() {
247 List<String> getStreams() {
248 return new ArrayList<>(streams);
251 int getPingInterval() {
255 String getPingMessage() {
259 File getKeystorePath() {
263 String getKeystorePassword() {
264 return keystorePassword;
267 File getTruststorePath() {
268 return truststorePath;
271 String getTruststorePassword() {
272 return truststorePassword;
275 List<String> getIncludedProtocols() {
276 return new ArrayList<>(includedProtocols);
279 List<String> getExcludedProtocols() {
280 return new ArrayList<>(excludedProtocols);
283 List<String> getIncludedCipherSuites() {
284 return new ArrayList<>(includedCipherSuites);
287 List<String> getExcludedCipherSuites() {
288 return new ArrayList<>(excludedCipherSuites);
291 boolean isTrustAll() {
295 boolean isRegenerationAllowed() {
296 return regenerationAllowed;
299 int getThreadPoolSize() {
300 return threadPoolSize;
303 Credentials getCredentials() {