1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\r
4 * * ===========================================================================
\r
5 * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.
\r
6 * * Copyright © 2017 Amdocs
\r
7 * * ===========================================================================
\r
8 * * Licensed under the Apache License, Version 2.0 (the "License");
\r
9 * * you may not use this file except in compliance with the License.
\r
10 * * You may obtain a copy of the License at
\r
12 * * http://www.apache.org/licenses/LICENSE-2.0
\r
14 * * Unless required by applicable law or agreed to in writing, software
\r
15 * * distributed under the License is distributed on an "AS IS" BASIS,
\r
16 * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
17 * * See the License for the specific language governing permissions and
\r
18 * * limitations under the License.
\r
19 * * ============LICENSE_END====================================================
\r
21 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
23 ******************************************************************************/
\r
24 package com.att.cmd;
\r
26 import java.io.BufferedReader;
\r
27 import java.io.Console;
\r
28 import java.io.File;
\r
29 import java.io.FileReader;
\r
30 import java.io.IOException;
\r
31 import java.io.InputStream;
\r
32 import java.io.InputStreamReader;
\r
33 import java.io.OutputStreamWriter;
\r
34 import java.io.PrintWriter;
\r
35 import java.io.Reader;
\r
36 import java.io.Writer;
\r
37 import java.net.HttpURLConnection;
\r
38 import java.util.ArrayList;
\r
39 import java.util.List;
\r
40 import java.util.Properties;
\r
42 import org.apache.log4j.PropertyConfigurator;
\r
44 import com.att.aft.dme2.api.DME2Manager;
\r
45 import com.att.authz.env.AuthzEnv;
\r
46 import com.att.cadi.Access.Level;
\r
47 import com.att.cadi.CadiException;
\r
48 import com.att.cadi.Locator;
\r
49 import com.att.cadi.SecuritySetter;
\r
50 import com.att.cadi.client.PropertyLocator;
\r
51 import com.att.cadi.client.Retryable;
\r
52 import com.att.cadi.config.Config;
\r
53 import com.att.cadi.config.SecurityInfo;
\r
54 import com.att.cadi.config.SecurityInfoC;
\r
55 import com.att.cadi.dme2.DME2Locator;
\r
56 import com.att.cadi.filter.AccessGetter;
\r
57 import com.att.cadi.http.HBasicAuthSS;
\r
58 import com.att.cadi.http.HMangr;
\r
59 import com.att.cmd.mgmt.Mgmt;
\r
60 import com.att.cmd.ns.NS;
\r
61 import com.att.cmd.perm.Perm;
\r
62 import com.att.cmd.role.Role;
\r
63 import com.att.cmd.user.User;
\r
64 import com.att.inno.env.APIException;
\r
65 import com.att.inno.env.Env;
\r
66 import com.att.inno.env.impl.Log4JLogTarget;
\r
67 import com.att.inno.env.util.Split;
\r
69 import jline.console.ConsoleReader;
\r
71 public class AAFcli {
\r
73 public static final String AAF_DEFAULT_REALM = "aaf_default_realm";
\r
74 protected static PrintWriter pw;
\r
75 protected HMangr hman;
\r
76 // Storage for last reused client. We can do this
\r
77 // because we're technically "single" threaded calls.
\r
78 public Retryable<?> prevCall;
\r
80 protected SecuritySetter<HttpURLConnection> ss;
\r
81 protected AuthzEnv env;
\r
82 private boolean close;
\r
83 private List<Cmd> cmds;
\r
86 private ArrayList<Integer> expect = new ArrayList<Integer>();
\r
87 private boolean verbose = true;
\r
89 private SecurityInfo si;
\r
90 private boolean request = false;
\r
91 private String force = null;
\r
92 private boolean gui = false;
\r
94 private static int TIMEOUT = Integer.parseInt(Config.AAF_CONN_TIMEOUT_DEF);
\r
95 private static boolean isConsole = false;
\r
96 private static boolean isTest = false;
\r
97 private static boolean showDetails = false;
\r
98 private static boolean ignoreDelay = false;
\r
99 private static int globalDelay=0;
\r
101 public static int timeout() {
\r
105 public AAFcli(AuthzEnv env, Writer wtr, HMangr hman, SecurityInfo si, SecuritySetter<HttpURLConnection> ss) throws APIException {
\r
110 if (wtr instanceof PrintWriter) {
\r
111 pw = (PrintWriter) wtr;
\r
114 pw = new PrintWriter(wtr);
\r
119 // client = new DRcli(new URI(aafurl), new
\r
120 // BasicAuth(user,toPass(pass,true)))
\r
121 // .apiVersion("2.0")
\r
122 // .timeout(TIMEOUT);
\r
127 cmds = new ArrayList<Cmd>();
\r
129 Role role = new Role(this);
\r
130 cmds.add(new Help(this, cmds));
\r
131 cmds.add(new Version(this));
\r
132 cmds.add(new Perm(role));
\r
134 cmds.add(new User(this));
\r
135 cmds.add(new NS(this));
\r
136 cmds.add(new Mgmt(this));
\r
139 public void verbose(boolean v) {
\r
143 public void close() {
\r
144 if (hman != null) {
\r
153 public boolean eval(String line) throws Exception {
\r
154 if (line.length() == 0) {
\r
156 } else if (line.startsWith("#")) {
\r
161 String[] largs = argEval(line);
\r
164 // Variable replacement
\r
165 StringBuilder sb = null;
\r
166 while (idx < largs.length) {
\r
168 for (int v = largs[idx].indexOf("@["); v >= 0; v = largs[idx].indexOf("@[", v + 1)) {
\r
170 sb = new StringBuilder();
\r
172 sb.append(largs[idx], e, v);
\r
173 if ((e = largs[idx].indexOf(']', v)) >= 0) {
\r
174 String p = env.getProperty(largs[idx].substring(v + 2, e++));
\r
180 if (sb != null && sb.length() > 0) {
\r
181 sb.append(largs[idx], e, largs[idx].length());
\r
182 largs[idx] = sb.toString();
\r
190 while (rv && idx < largs.length) {
\r
191 // Allow Script to change Credential
\r
193 if("as".equalsIgnoreCase(largs[idx])) {
\r
194 if (largs.length > ++idx) {
\r
195 // get Password from Props with ID as Key
\r
196 String user = largs[idx++];
\r
197 int colon = user.indexOf(':');
\r
200 pass = user.substring(colon + 1);
\r
201 user = user.substring(0, colon);
\r
203 pass = env.getProperty(user);
\r
206 if (pass != null) {
\r
207 pass = env.decrypt(pass, false);
\r
208 env.setProperty(user, pass);
\r
209 ss = new HBasicAuthSS(user, pass,(SecurityInfoC<HttpURLConnection>) si);
\r
210 pw.println("as " + user);
\r
211 } else { // get Pass from System Properties, under name of
\r
213 pw.println("ERROR: No password set for " + user);
\r
218 } else if ("expect".equalsIgnoreCase(largs[idx])) {
\r
220 if (largs.length > idx++) {
\r
221 if (!"nothing".equals(largs[idx])) {
\r
222 for (String str : largs[idx].split(",")) {
\r
224 if ("Exception".equalsIgnoreCase(str)) {
\r
227 expect.add(Integer.parseInt(str));
\r
229 } catch (NumberFormatException e) {
\r
230 throw new CadiException("\"expect\" should be followed by Number");
\r
237 // Sleep, typically for reports, to allow DB to update
\r
240 } else if ("sleep".equalsIgnoreCase(largs[idx])) {
\r
241 Integer t = Integer.parseInt(largs[++idx]);
\r
242 pw.println("sleep " + t);
\r
246 } else if ("delay".equalsIgnoreCase(largs[idx])) {
\r
247 delay = Integer.parseInt(largs[++idx]);
\r
248 pw.println("delay " + delay);
\r
251 } else if ("pause".equalsIgnoreCase(largs[idx])) {
\r
252 pw.println("Press <Return> to continue...");
\r
254 new BufferedReader(new InputStreamReader(System.in)).readLine();
\r
256 } else if ("exit".equalsIgnoreCase(largs[idx])) {
\r
257 pw.println("Exiting...");
\r
263 if("REQUEST".equalsIgnoreCase(largs[idx])) {
\r
266 } else if("FORCE".equalsIgnoreCase(largs[idx])) {
\r
269 } else if ("set".equalsIgnoreCase(largs[idx])) {
\r
270 while (largs.length > ++idx) {
\r
271 int equals = largs[idx].indexOf('=');
\r
275 String tag = largs[idx].substring(0, equals);
\r
276 String value = largs[idx].substring(++equals);
\r
277 pw.println("set " + tag + ' ' + value);
\r
278 boolean isTrue = "TRUE".equalsIgnoreCase(value);
\r
279 if("FORCE".equalsIgnoreCase(tag)) {
\r
281 } else if("REQUEST".equalsIgnoreCase(tag)) {
\r
283 } else if("DETAILS".equalsIgnoreCase(tag)) {
\r
284 showDetails = isTrue;
\r
286 env.setProperty(tag, value);
\r
290 // Allow Script to indicate if Failure is what is expected
\r
294 for (Cmd c : cmds) {
\r
295 if (largs[idx].equalsIgnoreCase(c.getName())) {
\r
298 if (expect.size() > 0) {
\r
299 pw.print("** Expect ");
\r
300 boolean first = true;
\r
301 for (Integer i : expect) {
\r
313 ret = c.exec(++idx, largs);
\r
314 if (delay+globalDelay > 0) {
\r
315 Thread.sleep(delay+globalDelay);
\r
317 } catch (Exception e) {
\r
318 if (expect.contains(-1)) {
\r
319 pw.println(e.getMessage());
\r
325 clearSingleLineProperties();
\r
327 rv = expect.isEmpty() ? true : expect.contains(ret);
\r
332 pw.print("!!! Unexpected Return Code: ");
\r
334 pw.println(", VALIDATE OUTPUT!!!");
\r
340 pw.write("Unknown Instruction \"");
\r
341 pw.write(largs[idx]);
\r
343 idx = largs.length;// always end after one command
\r
348 private String[] argEval(String line) {
\r
349 StringBuilder sb = new StringBuilder();
\r
350 ArrayList<String> arr = new ArrayList<String>();
\r
351 boolean start = true;
\r
353 for (int i = 0; i < line.length(); ++i) {
\r
355 if (Character.isWhitespace(ch = line.charAt(i))) {
\r
358 } else if (quote != 0) {
\r
361 arr.add(sb.toString());
\r
365 } else if (ch == '\'' || ch == '"') { // toggle
\r
376 if (sb.length() > 0) {
\r
377 arr.add(sb.toString());
\r
380 String[] rv = new String[arr.size()];
\r
385 public static void keyboardHelp() {
\r
386 System.out.println("'C-' means hold the ctrl key down while pressing the next key.");
\r
387 System.out.println("'M-' means hold the alt key down while pressing the next key.");
\r
388 System.out.println("For instance, C-b means hold ctrl key and press b, M-b means hold alt and press b\n");
\r
390 System.out.println("Basic Keybindings:");
\r
391 System.out.println("\tC-l - clear screen");
\r
392 System.out.println("\tC-a - beginning of line");
\r
393 System.out.println("\tC-e - end of line");
\r
394 System.out.println("\tC-b - backward character (left arrow also works)");
\r
395 System.out.println("\tM-b - backward word");
\r
396 System.out.println("\tC-f - forward character (right arrow also works)");
\r
397 System.out.println("\tM-f - forward word");
\r
398 System.out.println("\tC-d - delete character under cursor");
\r
399 System.out.println("\tM-d - delete word forward");
\r
400 System.out.println("\tM-backspace - delete word backward");
\r
401 System.out.println("\tC-k - delete from cursor to end of line");
\r
402 System.out.println("\tC-u - delete entire line, regardless of cursor position\n");
\r
404 System.out.println("Command History:");
\r
405 System.out.println("\tC-r - search backward in history (repeating C-r continues the search)");
\r
406 System.out.println("\tC-p - move backwards through history (up arrow also works)");
\r
407 System.out.println("\tC-n - move forwards through history (down arrow also works)\n");
\r
414 public static void main(String[] args) {
\r
416 // Cover for bash's need to escape *... (\\*)
\r
417 for (int i = 0; i < args.length; ++i) {
\r
418 if ("\\*".equals(args[i])) {
\r
423 System.setProperty("java.util.logging.config.file", "etc/logging.props");
\r
424 final AuthzEnv env = new AuthzEnv(System.getProperties());
\r
426 // Stop the (exceedingly annoying) DME2/other logs from printing console
\r
429 // Load Log4j too... sigh
\r
430 is = ClassLoader.getSystemResourceAsStream("log4j.properties");
\r
432 env.log(Level.WARN, "Cannot find 'log4j.properties' in Classpath. Best option: add 'etc' directory to classpath");
\r
435 Properties props = new Properties();
\r
437 PropertyConfigurator.configure(props);
\r
438 } catch (Exception e) {
\r
439 e.printStackTrace();
\r
443 } catch (IOException e) {
\r
444 env.debug().log(e); // only logging to avoid Sonar False positives.
\r
449 env.loadFromSystemPropsStartsWith("AFT", "DME2", "aaf", "keyfile");
\r
451 Log4JLogTarget.setLog4JEnv("aaf", env);
\r
452 GetProp gp = new GetProp(env);
\r
453 String user = gp.get(false,Config.AAF_MECHID,"fully qualified id");
\r
454 String pass = gp.get(true, Config.AAF_MECHPASS, "password is hidden");
\r
455 if(env.getProperty(Config.AAF_URL)==null) {
\r
456 String p = env.getProperty("DMEServiceName");
\r
458 boolean https = "true".equalsIgnoreCase(env.getProperty("AFT_DME2_SSL_ENABLE"));
\r
459 env.setProperty(Config.AAF_URL, "http"+(https?"s":"")+"://DME2RESOLVE/"+p);
\r
462 String aafUrl = gp.get(false, Config.AAF_URL, "https://DME2RESOLVE or Direct URL:port");
\r
464 if(aafUrl!=null && aafUrl.contains("//DME2")) {
\r
465 //gp.set(Config.AFT_LATITUDE,"Lookup from a Map App or table");
\r
466 //gp.set(Config.AFT_LONGITUDE,"Lookup from a Map App or table");
\r
467 //gp.set(Config.AFT_ENVIRONMENT,"Check DME2 Installations");
\r
470 if (gp.err() != null) {
\r
471 gp.err().append("to continue...");
\r
472 System.err.println(gp.err());
\r
478 boolean exitOnFailure = true;
\r
480 * Check for "-" options anywhere in command line
\r
482 StringBuilder sb = new StringBuilder();
\r
483 for (int i = 0; i < args.length; ++i) {
\r
484 if ("-i".equalsIgnoreCase(args[i])) {
\r
485 rdr = new InputStreamReader(System.in);
\r
486 // } else if("-o".equalsIgnoreCase(args[i])) {
\r
487 // // shall we do something different? Output stream is
\r
489 } else if ("-f".equalsIgnoreCase(args[i])) {
\r
490 if (args.length > i + 1) {
\r
491 rdr = new FileReader(args[++i]);
\r
493 } else if ("-a".equalsIgnoreCase(args[i])) {
\r
494 exitOnFailure = false;
\r
495 } else if ("-c".equalsIgnoreCase(args[i])) {
\r
497 } else if ("-s".equalsIgnoreCase(args[i]) && args.length > i + 1) {
\r
498 env.setProperty(Cmd.STARTDATE, args[++i]);
\r
499 } else if ("-e".equalsIgnoreCase(args[i]) && args.length > i + 1) {
\r
500 env.setProperty(Cmd.ENDDATE, args[++i]);
\r
501 } else if ("-t".equalsIgnoreCase(args[i])) {
\r
503 } else if ("-d".equalsIgnoreCase(args[i])) {
\r
504 showDetails = true;
\r
505 } else if ("-n".equalsIgnoreCase(args[i])) {
\r
506 ignoreDelay = true;
\r
508 if (sb.length() > 0) {
\r
511 sb.append(args[i]);
\r
515 SecurityInfo si = new SecurityInfo(env);
\r
516 env.loadToSystemPropsStartsWith("AAF", "DME2");
\r
518 if(aafUrl.contains("//DME2RESOLVE")) {
\r
519 DME2Manager dm = new DME2Manager("AAFcli DME2Manager", System.getProperties());
\r
520 loc = new DME2Locator(env, dm, aafUrl);
\r
522 loc = new PropertyLocator(aafUrl);
\r
525 //Config.configPropFiles(new AccessGetter(env), env);
\r
527 TIMEOUT = Integer.parseInt(env.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
\r
528 HMangr hman = new HMangr(env, loc).readTimeout(TIMEOUT).apiVersion("2.0");
\r
530 //TODO: Consider requiring a default in properties
\r
531 env.setProperty(Config.AAF_DEFAULT_REALM, System.getProperty(Config.AAF_DEFAULT_REALM,Config.getDefaultRealm()));
\r
533 AAFcli aafcli = new AAFcli(env, new OutputStreamWriter(System.out), hman, si,
\r
534 new HBasicAuthSS(user, env.decrypt(pass,false), (SecurityInfoC<HttpURLConnection>) si));
\r
536 File delay = new File("aafcli.delay");
\r
537 if(delay.exists()) {
\r
538 BufferedReader br = new BufferedReader(new FileReader(delay));
\r
540 globalDelay = Integer.parseInt(br.readLine());
\r
541 } catch(Exception e) {
\r
542 env.debug().log(e);
\r
550 System.out.println("Type 'help' for short help or 'help -d' for detailed help with aafcli commands");
\r
551 System.out.println("Type '?' for help with command line editing");
\r
552 System.out.println("Type 'q', 'quit', or 'exit' to quit aafcli\n");
\r
554 ConsoleReader reader = new ConsoleReader();
\r
556 reader.setPrompt("aafcli > ");
\r
559 while ((line = reader.readLine()) != null) {
\r
560 showDetails = (line.contains("-d"))?true:false;
\r
562 if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("q") || line.equalsIgnoreCase("exit")) {
\r
564 } else if (line.equalsIgnoreCase("--help -d") || line.equalsIgnoreCase("help -d")
\r
565 || line.equalsIgnoreCase("help")) {
\r
567 } else if (line.equalsIgnoreCase("cls")) {
\r
568 reader.clearScreen();
\r
570 } else if (line.equalsIgnoreCase("?")) {
\r
577 } catch (Exception e) {
\r
578 pw.println(e.getMessage());
\r
585 } else if (rdr != null) {
\r
586 BufferedReader br = new BufferedReader(rdr);
\r
588 while ((line = br.readLine()) != null) {
\r
589 if (!aafcli.eval(line) && exitOnFailure) {
\r
594 } else { // just run the command line
\r
595 aafcli.verbose(false);
\r
596 if (sb.length() == 0) {
\r
597 sb.append("--help");
\r
599 rv = aafcli.eval(sb.toString()) ? 0 : 1;
\r
604 // Don't close if No Reader, or it's a Reader of Standard In
\r
605 if (rdr != null && !(rdr instanceof InputStreamReader)) {
\r
609 } catch (MessageException e) {
\r
610 System.out.println("MessageException caught");
\r
612 System.err.println(e.getMessage());
\r
613 } catch (Exception e) {
\r
614 e.printStackTrace(System.err);
\r
620 private static class GetProp {
\r
621 private Console cons = System.console();
\r
622 private StringBuilder err = null;
\r
623 private AuthzEnv env;
\r
625 public GetProp(AuthzEnv env) {
\r
629 public String get(final boolean pass, final String tag, final String other) {
\r
630 String data = env.getProperty(tag,null);
\r
631 if (data == null) {
\r
634 char[] cp = System.console().readPassword("%s: ",tag);
\r
636 data=String.valueOf(cp);
\r
639 cons.writer().format("%s: ", tag);
\r
641 data = cons.readLine();
\r
646 err = new StringBuilder("Add -D");
\r
648 err.append(", -D");
\r
661 public void set(final String tag, final String other) {
\r
662 String data = env.getProperty(tag,null);
\r
663 if (data == null) {
\r
665 cons.writer().format("%s: ", tag);
\r
667 data = cons.readLine();
\r
671 err = new StringBuilder("Add -D");
\r
673 err.append(", -D");
\r
684 System.setProperty(tag, data);
\r
688 public StringBuilder err() {
\r
693 public boolean isTest() {
\r
694 return AAFcli.isTest;
\r
697 public boolean isDetailed() {
\r
698 return AAFcli.showDetails;
\r
701 public String typeString(Class<?> cls, boolean json) {
\r
702 return "application/" + cls.getSimpleName() + "+" + (json ? "json" : "xml") + ";version=" + hman.apiVersion();
\r
705 public String forceString() {
\r
709 public boolean addRequest() {
\r
713 public void clearSingleLineProperties() {
\r
716 showDetails = false;
\r
719 public void gui(boolean b) {
\r