Update Fixes from testing
[aaf/authz.git] / auth / auth-cmd / src / main / java / org / onap / aaf / auth / cmd / AAFcli.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  *
7  * Modifications Copyright (C) 2018 IBM.
8  * ===========================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  * 
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  * 
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END====================================================
21  *
22  */
23
24 package org.onap.aaf.auth.cmd;
25
26 import java.io.BufferedReader;
27 import java.io.File;
28 import java.io.FileReader;
29 import java.io.InputStreamReader;
30 import java.io.OutputStreamWriter;
31 import java.io.PrintWriter;
32 import java.io.Reader;
33 import java.io.Writer;
34 import java.net.HttpURLConnection;
35 import java.util.ArrayList;
36 import java.util.List;
37
38 import org.onap.aaf.auth.cmd.mgmt.Mgmt;
39 import org.onap.aaf.auth.cmd.ns.NS;
40 import org.onap.aaf.auth.cmd.perm.Perm;
41 import org.onap.aaf.auth.cmd.role.Role;
42 import org.onap.aaf.auth.cmd.user.User;
43 import org.onap.aaf.auth.common.Define;
44 import org.onap.aaf.auth.env.AuthzEnv;
45 import org.onap.aaf.cadi.Access;
46 import org.onap.aaf.cadi.Access.Level;
47 import org.onap.aaf.cadi.CadiException;
48 import org.onap.aaf.cadi.PropAccess;
49 import org.onap.aaf.cadi.SecuritySetter;
50 import org.onap.aaf.cadi.aaf.v2_0.AAFConHttp;
51 import org.onap.aaf.cadi.client.Retryable;
52 import org.onap.aaf.cadi.config.Config;
53 import org.onap.aaf.cadi.config.SecurityInfoC;
54 import org.onap.aaf.cadi.http.HBasicAuthSS;
55 import org.onap.aaf.cadi.http.HMangr;
56 import org.onap.aaf.cadi.sso.AAFSSO;
57 import org.onap.aaf.misc.env.APIException;
58
59 import jline.console.ConsoleReader;
60
61 public class AAFcli {
62     protected static PrintWriter pw;
63     protected HMangr hman;
64     // Storage for last reused client. We can do this
65     // because we're technically "single" threaded calls.
66     public Retryable<?> prevCall;
67
68     protected SecuritySetter<HttpURLConnection> ss;
69 //    protected AuthzEnv env;
70     private boolean close;
71     private List<Cmd> cmds;
72
73     // Lex State
74     private ArrayList<Integer> expect = new ArrayList<>();
75     private boolean verbose = true;
76     private int delay;
77     private SecurityInfoC<HttpURLConnection> si;
78     private boolean request = false;
79     private String force = null;
80     private boolean gui = false;
81     // Package on purpose
82     Access access;
83     AuthzEnv env;
84
85     private static int TIMEOUT = Integer.parseInt(Config.AAF_CONN_TIMEOUT_DEF);
86     private static boolean isConsole = false;
87     private static boolean isTest = false;
88     private static boolean showDetails = false;
89     private static boolean ignoreDelay = false;
90     private static int globalDelay=0;
91
92     // Create when only have Access
93     public AAFcli(Access access, Writer wtr, HMangr hman, SecurityInfoC<HttpURLConnection> si, SecuritySetter<HttpURLConnection> ss) throws APIException, CadiException {
94         this(access,new AuthzEnv(access.getProperties()),wtr,hman, si,ss);
95     }
96
97     public AAFcli(Access access, AuthzEnv env, Writer wtr, HMangr hman, SecurityInfoC<HttpURLConnection> si, SecuritySetter<HttpURLConnection> ss) throws APIException {
98         this.env = env;
99         this.access = access;
100         this.ss = ss;
101         this.hman = hman;
102         this.si = si;
103         if (wtr instanceof PrintWriter) {
104             pw = (PrintWriter) wtr;
105             close = false;
106         } else {
107             pw = new PrintWriter(wtr);
108             close = true;
109         }
110
111         /*
112          * Create Cmd Tree
113          */
114         cmds = new ArrayList<>();
115
116         Role role = new Role(this);
117         cmds.add(new Help(this, cmds));
118         cmds.add(new Version(this));
119         cmds.add(new Perm(role));
120         cmds.add(role);
121         cmds.add(new User(this));
122         cmds.add(new NS(this));
123         cmds.add(new Mgmt(this));
124     }
125
126     public AuthzEnv env() {
127         return env;
128     }
129
130     public static int timeout() {
131         return TIMEOUT;
132     }
133
134     public void verbose(boolean v) {
135         verbose = v;
136     }
137
138     public void close() {
139 //        if (hman != null) {
140 //            hman.close();
141 //            hman = null;
142 //        }
143         if (close) {
144             pw.close();
145         }
146     }
147
148     public boolean eval(String line) throws Exception {
149         if (line.length() == 0) {
150             return true;
151         } else if (line.startsWith("#")) {
152             pw.println(line);
153             return true;
154         }
155
156         String[] largs = argEval(line);
157         int idx = 0;
158
159         // Variable replacement
160         StringBuilder sb = null;
161         while (idx < largs.length) {
162             int e = 0;
163             for (int v = largs[idx].indexOf("@["); v >= 0; v = largs[idx].indexOf("@[", v + 1)) {
164                 if (sb == null) {
165                     sb = new StringBuilder();
166                 }
167                 sb.append(largs[idx], e, v);
168                 if ((e = largs[idx].indexOf(']', v)) >= 0) {
169                     String p = access.getProperty(largs[idx].substring(v + 2, e),null);
170                     if (p==null) {
171                         p = System.getProperty(largs[idx].substring(v+2,e));
172                     }
173                     ++e;
174                     if (p != null) {
175                         sb.append(p);
176                     }
177                 }
178             }
179             if (sb != null && sb.length() > 0) {
180                 sb.append(largs[idx], e, largs[idx].length());
181                 largs[idx] = sb.toString();
182                 sb.setLength(0);
183             }
184             ++idx;
185         }
186
187         idx = 0;
188         boolean rv = true;
189         while (rv && idx < largs.length) {
190             // Allow Script to change Credential
191             if (!gui) {
192                 if ("as".equalsIgnoreCase(largs[idx])) {
193                     if (largs.length > ++idx) {
194                         // get Password from Props with ID as Key
195                         String user = largs[idx++];
196                         int colon = user.indexOf(':');
197                         String pass;
198                         if (colon > 0) {
199                             pass = user.substring(colon + 1);
200                             user = user.substring(0, colon);
201                         } else {
202                             pass = access.getProperty(user, null);
203                         }
204                         if (pass != null) {
205                             pass = access.decrypt(pass, false);
206                             access.getProperties().put(user, pass);
207                             ss=new HBasicAuthSS(si, user, pass);
208                             pw.println("as " + user);
209                         } else { // get Pass from System Properties, under name of
210                             // Tag
211                             pw.println("ERROR: No password set for " + user);
212                             rv = false;
213                         }
214                         continue;
215                     }
216                 } else if ("expect".equalsIgnoreCase(largs[idx])) {
217                     expect.clear();
218                     if (largs.length > idx++) {
219                         if (!"nothing".equals(largs[idx])) {
220                             for (String str : largs[idx].split(",")) {
221                                 try {
222                                     if ("Exception".equalsIgnoreCase(str)) {
223                                         expect.add(-1);
224                                     } else {
225                                         expect.add(Integer.parseInt(str));
226                                     }
227                                 } catch (NumberFormatException e) {
228                                     throw new CadiException("\"expect\" should be followed by Number");
229                                 }
230                             }
231                         ++idx;
232                         }
233                     }
234                     continue;
235                     // Sleep, typically for reports, to allow DB to update
236                     // Milliseconds
237                     
238                 } else if ("sleep".equalsIgnoreCase(largs[idx])) {
239                     Integer t = Integer.parseInt(largs[++idx]);
240                     pw.println("sleep " + t);
241                     Thread.sleep(t);
242                     ++idx;
243                     continue;
244                 } else if ("delay".equalsIgnoreCase(largs[idx])) {
245                     delay = Integer.parseInt(largs[++idx]);
246                     pw.println("delay " + delay);
247                     ++idx;
248                     continue;
249                 } else if ("pause".equalsIgnoreCase(largs[idx])) {
250                     pw.println("Press <Return> to continue...");
251                     ++idx;
252                     // Sonar insists we do something with the string, though it's only a pause.  Not very helpful...
253                     BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
254                     String sonar;
255                     try {
256                         sonar = br.readLine();
257                     } finally {
258                         br.close();
259                     }
260                     sonar=""; // this useless code brought to you by Sonar.
261                     pw.print(sonar);
262                     continue;
263                 } else if ("exit".equalsIgnoreCase(largs[idx])) {
264                     pw.println("Exiting...");
265                     return false;
266                 } else if ("set".equalsIgnoreCase(largs[idx])) {
267                     while (largs.length > ++idx) {
268                         int equals = largs[idx].indexOf('=');
269                         String tag, value;
270                         if (equals < 0) {
271                             tag = largs[idx];
272                             value = access.getProperty(Config.AAF_APPPASS,null);
273                             if (value==null) {
274                                 break;
275                             } else {
276                                 value = access.decrypt(value, false);
277                                 if (value==null) {
278                                     break;
279                                 }
280                                 access.getProperties().put(tag, value);
281                                 pw.println("set " + tag + " <encrypted>");
282                             }
283                         } else {
284                             tag = largs[idx].substring(0, equals);
285                             value = largs[idx].substring(++equals);
286                             pw.println("set " + tag + ' ' + value);
287                         }
288                         boolean isTrue = "TRUE".equalsIgnoreCase(value);
289                         if ("FORCE".equalsIgnoreCase(tag)) {
290                             force = value;
291                         } else if ("REQUEST".equalsIgnoreCase(tag)) {
292                             request = isTrue;
293                         } else if ("DETAILS".equalsIgnoreCase(tag)) {
294                             showDetails = isTrue;
295                         } else {
296                             access.getProperties().put(tag, value);
297                         }
298                     }
299                     continue;
300                     // Allow Script to indicate if Failure is what is expected
301                 }
302
303             } 
304             
305             if ("REQUEST".equalsIgnoreCase(largs[idx])) {
306                 request=true;
307                 ++idx;
308             } else if ("FORCE".equalsIgnoreCase(largs[idx])) {
309                 force="true";
310                 ++idx;
311             } else if ("DETAILS".equalsIgnoreCase(largs[idx])) {
312                 showDetails=true;
313                 ++idx;
314             }
315
316             int ret = 0;
317             for (Cmd c : cmds) {
318                 if (largs[idx].equalsIgnoreCase(c.getName())) {
319                     if (verbose) {
320                         pw.println(line);
321                         if (expect.size() > 0) {
322                             pw.print("** Expect ");
323                             boolean first = true;
324                             for (Integer i : expect) {
325                                 if (first) {
326                                     first = false;
327                                 } else {
328                                     pw.print(',');
329                                 }
330                                 pw.print(i);
331                             }
332                             pw.println(" **");
333                         }
334                     }
335                     try {
336                         ret = c.exec(++idx, largs);
337                         if (delay+globalDelay > 0) {
338                             Thread.sleep((long)(delay+globalDelay));
339                         }
340                     } catch (Exception e) {
341                         if (expect.contains(-1)) {
342                             pw.println(e.getMessage());
343                             ret = -1;
344                         } else {
345                             throw e;
346                         }
347                     } finally {
348                         clearSingleLineProperties();
349                     }
350                     rv = expect.isEmpty() || expect.contains(ret);
351                     if (verbose) {
352                         if (rv) {
353                             pw.println();
354                         } else {
355                             pw.print("!!! Unexpected Return Code: ");
356                             pw.print(ret);
357                             pw.println(", VALIDATE OUTPUT!!!");
358                         }
359                     }
360                     return rv;
361                 }
362             }
363             pw.write("Unknown Instruction \"");
364             pw.write(largs[idx]);
365             pw.write("\"\n");
366             idx = largs.length;// always end after one command
367         }
368         return rv;
369     }
370
371     private String[] argEval(String line) {
372         StringBuilder sb = new StringBuilder();
373         ArrayList<String> arr = new ArrayList<>();
374         boolean start = true;
375         char quote = 0;
376         char last = 0;
377         for (int i = 0; i < line.length(); ++i) {
378             char ch;
379             if (Character.isWhitespace(ch = line.charAt(i))) {
380                 if (start || last==',') {
381                     continue; // trim
382                 } else if (quote != 0) {
383                     sb.append(ch);
384                 } else {
385                     arr.add(sb.toString());
386                     sb.setLength(0);
387                     start = true;
388                 }
389             } else if (ch == '\'' || ch == '"') { // toggle
390                 if (quote == ch) {
391                     quote = 0;
392                 } else {
393                     quote = ch;
394                 }
395             } else if (ch=='|' && quote==0) {
396                 arr.add(sb.toString());
397                 sb.setLength(0);
398                 start = true;
399             } else {
400                 start = false;
401                 sb.append(ch);
402                 last = ch;
403             }
404         }
405         if (sb.length() > 0) {
406             arr.add(sb.toString());
407         }
408
409         String[] rv = new String[arr.size()];
410         arr.toArray(rv);
411         return rv;
412     }
413
414     public static void keyboardHelp() {
415         System.out.println("'C-' means hold the ctrl key down while pressing the next key.");
416         System.out.println("'M-' means hold the alt key down while pressing the next key.");
417         System.out.println("For instance, C-b means hold ctrl key and press b, M-b means hold alt and press b\n");
418
419         System.out.println("Basic Keybindings:");
420         System.out.println("\tC-l - clear screen");        
421         System.out.println("\tC-a - beginning of line");
422         System.out.println("\tC-e - end of line");
423         System.out.println("\tC-b - backward character (left arrow also works)");
424         System.out.println("\tM-b - backward word");
425         System.out.println("\tC-f - forward character (right arrow also works)");
426         System.out.println("\tM-f - forward word");
427         System.out.println("\tC-d - delete character under cursor");
428         System.out.println("\tM-d - delete word forward");
429         System.out.println("\tM-backspace - delete word backward");
430         System.out.println("\tC-k - delete from cursor to end of line");
431         System.out.println("\tC-u - delete entire line, regardless of cursor position\n");
432
433         System.out.println("Command History:");
434         System.out.println("\tC-r - search backward in history (repeating C-r continues the search)");
435         System.out.println("\tC-p - move backwards through history (up arrow also works)");
436         System.out.println("\tC-n - move forwards through history (down arrow also works)\n");
437
438     }
439
440     /**
441      * @param args
442      */
443     public static void main(String[] args) {
444         int rv = 0;
445         
446         try {
447             AAFSSO aafsso = new AAFSSO(args);
448             String noexit = aafsso.access().getProperty("no_exit");
449             try {
450                 PropAccess access = aafsso.access();
451
452                 if (aafsso.ok()) {
453                     Define.set(access);
454                     AuthzEnv env = new AuthzEnv(access);
455                     
456                     Reader rdr = null;
457                     boolean exitOnFailure = true;
458                     /*
459                      * Check for "-" options anywhere in command line
460                      */
461                     StringBuilder sb = new StringBuilder();
462                     for (int i = 0; i < args.length; ++i) {
463                         if ("-i".equalsIgnoreCase(args[i])) {
464                             rdr = new InputStreamReader(System.in);
465                             // } else if ("-o".equalsIgnoreCase(args[i])) {
466                             // // shall we do something different? Output stream is
467                             // already done...
468                         } else if ("-f".equalsIgnoreCase(args[i])) {
469                             if (args.length > i + 1) {
470                                 rdr = new FileReader(args[++i]);
471                             }
472                         } else if ("-a".equalsIgnoreCase(args[i])) {
473                             exitOnFailure = false;
474                         } else if ("-c".equalsIgnoreCase(args[i])) {
475                             isConsole = true;
476                         } else if ("-s".equalsIgnoreCase(args[i]) && args.length > i + 1) {
477                             access.setProperty(Cmd.STARTDATE, args[++i]);
478                         } else if ("-e".equalsIgnoreCase(args[i]) && args.length > i + 1) {
479                             access.setProperty(Cmd.ENDDATE, args[++i]);
480                         } else if ("-t".equalsIgnoreCase(args[i])) {
481                             isTest = true;
482                         } else if ("-d".equalsIgnoreCase(args[i])) {
483                             showDetails = true;
484                         } else if ("-n".equalsIgnoreCase(args[i])) {
485                             ignoreDelay = true;
486                         } else {
487                             if (sb.length() > 0) {
488                                 sb.append(' ');
489                             }
490                             sb.append(args[i]);
491                         }
492                     }
493                     
494                     AAFConHttp aafcon = new AAFConHttp(access);
495 //                    
496 //                    SecurityInfoC<?> si = aafcon.securityInfo();
497 //                    Locator<URI> loc;
498                     
499                     aafsso.setLogDefault();
500                     aafsso.setStdErrDefault();
501     
502                     // Note, with AAF Locator, this may not longer be necessary 3/2018 Jonathan
503                     if (!aafsso.loginOnly()) {
504 //                        try {
505 //                            loc = new AAFLocator(si,new URI(access.getProperty(Config.AAF_URL)));
506 //                        } catch (Throwable t) {
507 //                            aafsso.setStdErrDefault();
508 //                            throw t;
509 //                        } finally {
510 //                            // Other Access is done writing to StdOut and StdErr, reset Std out
511 //                            aafsso.setLogDefault();
512 //                        }
513     
514                         TIMEOUT = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
515 //                        HMangr hman = new HMangr(access, loc).readTimeout(TIMEOUT).apiVersion(Config.AAF_DEFAULT_API_VERSION);
516                         
517                         if (access.getProperty(Config.AAF_DEFAULT_REALM)==null) {
518                             access.setProperty(Config.AAF_DEFAULT_REALM, "people.osaaf.org");
519                             aafsso.addProp(Config.AAF_DEFAULT_REALM, "people.osaaf.org");
520                         }
521             
522                         AAFcli aafcli = new AAFcli(access,env, new OutputStreamWriter(System.out),  
523                                 aafcon.hman(), aafcon.securityInfo(), aafcon.securityInfo().defSS);
524 //                            new HBasicAuthSS(si,aafsso.user(), access.decrypt(aafsso.enc_pass(),false)));
525 //                        }
526                         if (!ignoreDelay) {
527                             File delay = new File("aafcli.delay");
528                             if (delay.exists()) {
529                                 BufferedReader br = new BufferedReader(new FileReader(delay));
530                                 try {
531                                     globalDelay = Integer.parseInt(br.readLine());
532                                 } catch (Exception e) {
533                                     access.log(Level.DEBUG,e);
534                                 } finally {
535                                     br.close();
536                                 }
537                             }
538                         }
539                         try {
540                             if (isConsole) {
541                                 System.out.println("Type 'help' for short help or 'help -d' for detailed help with aafcli commands");
542                                 System.out.println("Type '?' for help with command line editing");
543                                 System.out.println("Type 'q', 'quit', or 'exit' to quit aafcli\n");
544             
545                                 ConsoleReader reader = new ConsoleReader();
546                                 try {
547                                     reader.setPrompt("aafcli > ");
548                 
549                                     String line;
550                                     while ((line = reader.readLine()) != null) {
551                                         showDetails = (line.contains("-d"));
552                 
553                                         if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("q") || line.equalsIgnoreCase("exit")) {
554                                             break;
555                                         } else if (line.equalsIgnoreCase("--help -d") || line.equalsIgnoreCase("help -d") 
556                                                 || line.equalsIgnoreCase("help")) {
557                                             line = "--help";
558                                         } else if (line.equalsIgnoreCase("cls")) {
559                                             reader.clearScreen();
560                                             continue;
561                                         } else if (line.equalsIgnoreCase("?")) {
562                                             keyboardHelp();
563                                             continue;
564                                         }
565                                         try {
566                                             aafcli.eval(line);
567                                             pw.flush();
568                                         } catch (Exception e) {
569                                             pw.println(e.getMessage());
570                                             pw.flush();
571                                         }
572                                     }
573                                 } finally {
574                                     reader.close();
575                                 }
576                             } else if (rdr != null) {
577                                 BufferedReader br = new BufferedReader(rdr);
578                                 try {
579                                     String line;
580                                     while ((line = br.readLine()) != null) {
581                                         if (!aafcli.eval(line) && exitOnFailure) {
582                                             rv = 1;
583                                             break;
584                                         }
585                                     }
586                                 } finally {
587                                     br.close();
588                                 }
589                             } else { // just run the command line
590                                 aafcli.verbose(false);
591                                 if (sb.length() == 0) {
592                                     sb.append("--help");
593                                 }
594                                 rv = aafcli.eval(sb.toString()) ? 0 : 1;
595                             }
596                             
597                         } finally {
598                             aafcli.close();
599             
600                             // Don't close if No Reader, or it's a Reader of Standard In
601                             if (rdr != null && !(rdr instanceof InputStreamReader)) {
602                                 rdr.close();
603                             }
604                         }
605                     }
606                 }
607             } finally {
608                 aafsso.close();
609                 StringBuilder err = aafsso.err();
610                 if (err != null) {
611                     err.append("to continue...");
612                     System.err.println(err);
613                 }
614             }
615             if (noexit==null) {
616                 return;
617             }
618
619
620         } catch (MessageException e) {
621             System.out.println("MessageException caught");
622
623             System.err.println(e.getMessage());
624         } catch (Exception e) {
625             e.printStackTrace(System.err);
626         }
627         System.exit(rv);
628     }
629
630     public boolean isTest() {
631         return AAFcli.isTest;
632     }
633     
634     public boolean isDetailed() {
635         return AAFcli.showDetails;
636     }
637
638     public String typeString(Class<?> cls, boolean json) {
639         return "application/" + cls.getSimpleName() + "+" + (json ? "json" : "xml");//+ ";version=" + hman.apiVersion();
640     }
641
642     public String forceString() {
643         return force;
644     }
645
646     public boolean addRequest() {
647         return request;
648     }
649
650     public void clearSingleLineProperties() {
651         force  = null;
652         request = false;
653         showDetails = false;
654     }
655
656     public void gui(boolean b) {
657         gui  = b;
658     }
659
660 }