Bump odlparent to 6.0.0
[netconf.git] / restconf / websocket-client / src / main / java / org / opendaylight / restconf / websocket / client / ApplicationSettings.java
1 /*
2  * Copyright © 2019 FRINX s.r.o. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.restconf.websocket.client;
10
11 import com.google.common.base.Preconditions;
12 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
13 import java.io.File;
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;
26
27 /**
28  * Holder of the parsed user-input application arguments.
29  */
30 final class ApplicationSettings {
31
32     /**
33      * Credentials used for basic authentication - grouping of username and password.
34      */
35     static final class Credentials {
36         final String userName;
37         final String password;
38
39         private Credentials(final String userName, final String password) {
40             this.userName = userName;
41             this.password = password;
42         }
43
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);
53         }
54     }
55
56     private static final Logger LOG = LoggerFactory.getLogger(ApplicationSettings.class);
57     private static final ArgumentParser PARSER = ArgumentParsers.newFor("web-socket client test-tool").build();
58
59     static {
60         PARSER.addArgument("-l")
61                 .dest("loggingLevel")
62                 .required(false)
63                 .setDefault("INFO")
64                 .metavar("LEVEL")
65                 .type(String.class)
66                 .help("Logging level threshold used throughout the whole web-socket client.");
67         PARSER.addArgument("-s")
68                 .dest("streams")
69                 .required(true)
70                 .nargs("+")
71                 .help("Web-socket stream paths with ws or wss schemas.")
72                 .metavar("STREAM")
73                 .type(String.class);
74         PARSER.addArgument("-pi")
75                 .dest("pingInterval")
76                 .help("Interval in milliseconds between sending of ping web-socket frames to server. "
77                         + "Value of 0 disables ping process.")
78                 .metavar("INTERVAL")
79                 .setDefault(0)
80                 .type(Integer.class);
81         PARSER.addArgument("-pm")
82                 .dest("pingMessage")
83                 .help("Explicitly set ping message.")
84                 .metavar("MESSAGE")
85                 .setDefault("ping")
86                 .type(String.class);
87         PARSER.addArgument("-t")
88                 .dest("threads")
89                 .help("Explicitly set size of thread-pool used for holding of web-socket handlers and ping processes.")
90                 .metavar("SIZE")
91                 .setDefault(8)
92                 .type(Integer.class);
93         PARSER.addArgument("-r")
94                 .dest("regeneration")
95                 .help("Allowed TLS/SSL session regeneration.")
96                 .metavar("ALLOWED")
97                 .setDefault(false)
98                 .type(Boolean.class);
99         PARSER.addArgument("-kpath")
100                 .dest("keystorePath")
101                 .help("Path to the certificates key-store file.")
102                 .metavar("PATH")
103                 .type(File.class);
104         PARSER.addArgument("-kpass")
105                 .dest("keystorePassword")
106                 .help("Password used for unlocking of the certificates keystore.")
107                 .metavar("SECRET")
108                 .type(String.class);
109         PARSER.addArgument("-tpath")
110                 .dest("truststorePath")
111                 .help("Path to the certificates trust-store file.")
112                 .metavar("PATH")
113                 .type(File.class);
114         PARSER.addArgument("-tpass")
115                 .dest("truststorePassword")
116                 .help("Password used for unlocking of the certificates truststore.")
117                 .metavar("SECRET")
118                 .type(String.class);
119         PARSER.addArgument("-ta")
120                 .dest("trustAll")
121                 .help("All incoming certificates are trusted when both truststore and keystore are not specified.")
122                 .metavar("TRUST")
123                 .setDefault(false)
124                 .type(Boolean.class);
125         PARSER.addArgument("-ip")
126                 .dest("includedProtocols")
127                 .nargs("+")
128                 .help("Explicitly specified list of permitted versions of web-security protocols.")
129                 .metavar("PROTOCOL")
130                 .setDefault("TLSv1.2", "TLSv1.3")
131                 .type(String.class);
132         PARSER.addArgument("-ep")
133                 .dest("excludedProtocols")
134                 .nargs("*")
135                 .help("Explicitly specified list of denied versions of web-security protocols (denied protocols have "
136                         + "the highest priority).")
137                 .metavar("PROTOCOL")
138                 .setDefault("TLSv1", "TLSv1.1", "SSL", "SSLv2", "SSLv2Hello", "SSLv3")
139                 .type(String.class);
140         PARSER.addArgument("-ic")
141                 .dest("includedCipherSuites")
142                 .nargs("+")
143                 .help("Explicitly specified list of permitted cipher suites.")
144                 .metavar("CIPHER")
145                 .setDefault("TLS_ECDHE.*", "TLS_DHE_RSA.*")
146                 .type(String.class);
147         PARSER.addArgument("-ec")
148                 .dest("excludedCipherSuites")
149                 .nargs("*")
150                 .help("Explicitly specified list of denied cipher suites (denied ciphers have the highest priority).")
151                 .metavar("CIPHER")
152                 .setDefault(".*MD5.*", ".*RC4.*", ".*DSS.*", ".*NULL.*", ".*DES.*")
153                 .type(String.class);
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]")
158                 .type(String.class);
159     }
160
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;
193
194     private Credentials credentials;
195
196     private ApplicationSettings() {
197     }
198
199     /**
200      * Creation of application settings object using input command-line arguments (factory method).
201      *
202      * @param arguments Raw program arguments.
203      * @return Parsed arguments wrapped in {@link Optional} or {@link Optional#empty()} if only help is going
204      *     to be invoked.
205      */
206     static Optional<ApplicationSettings> parseApplicationSettings(final String[] arguments) {
207         final ApplicationSettings applicationSettings = new ApplicationSettings();
208         try {
209             PARSER.parseArgs(arguments, applicationSettings);
210             applicationSettings.verifyParsedArguments();
211             if (applicationSettings.basicAuthentication == null) {
212                 applicationSettings.credentials = null;
213             } else {
214                 applicationSettings.credentials = Credentials.extractCredentials(
215                     applicationSettings.basicAuthentication);
216             }
217         } catch (final ArgumentParserException | IllegalArgumentException e) {
218             if (e instanceof HelpScreenException) {
219                 return Optional.empty();
220             } else {
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);
227             }
228         }
229         LOG.info("Application settings {} have been parsed successfully.", (Object) arguments);
230         return Optional.of(applicationSettings);
231     }
232
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");
241     }
242
243     String getLoggingLevel() {
244         return loggingLevel;
245     }
246
247     List<String> getStreams() {
248         return new ArrayList<>(streams);
249     }
250
251     int getPingInterval() {
252         return pingInterval;
253     }
254
255     String getPingMessage() {
256         return pingMessage;
257     }
258
259     File getKeystorePath() {
260         return keystorePath;
261     }
262
263     String getKeystorePassword() {
264         return keystorePassword;
265     }
266
267     File getTruststorePath() {
268         return truststorePath;
269     }
270
271     String getTruststorePassword() {
272         return truststorePassword;
273     }
274
275     List<String> getIncludedProtocols() {
276         return new ArrayList<>(includedProtocols);
277     }
278
279     List<String> getExcludedProtocols() {
280         return new ArrayList<>(excludedProtocols);
281     }
282
283     List<String> getIncludedCipherSuites() {
284         return new ArrayList<>(includedCipherSuites);
285     }
286
287     List<String> getExcludedCipherSuites() {
288         return new ArrayList<>(excludedCipherSuites);
289     }
290
291     boolean isTrustAll() {
292         return trustAll;
293     }
294
295     boolean isRegenerationAllowed() {
296         return regenerationAllowed;
297     }
298
299     int getThreadPoolSize() {
300         return threadPoolSize;
301     }
302
303     Credentials getCredentials() {
304         return credentials;
305     }
306 }