AAFcli.java -Extract the assignment out of this expression
[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) 2019 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             ch = line.charAt(i);
380             if (Character.isWhitespace(ch)) {
381                 if (start || last==',') {
382                     continue; // trim
383                 } else if (quote != 0) {
384                     sb.append(ch);
385                 } else {
386                     arr.add(sb.toString());
387                     sb.setLength(0);
388                     start = true;
389                 }
390             } else if (ch == '\'' || ch == '"') { // toggle
391                 if (quote == ch) {
392                     quote = 0;
393                 } else {
394                     quote = ch;
395                 }
396             } else if (ch=='|' && quote==0) {
397                 arr.add(sb.toString());
398                 sb.setLength(0);
399                 start = true;
400             } else {
401                 start = false;
402                 sb.append(ch);
403                 last = ch;
404             }
405         }
406         if (sb.length() > 0) {
407             arr.add(sb.toString());
408         }
409
410         String[] rv = new String[arr.size()];
411         arr.toArray(rv);
412         return rv;
413     }
414
415     public static void keyboardHelp() {
416         System.out.println("'C-' means hold the ctrl key down while pressing the next key.");
417         System.out.println("'M-' means hold the alt key down while pressing the next key.");
418         System.out.println("For instance, C-b means hold ctrl key and press b, M-b means hold alt and press b\n");
419
420         System.out.println("Basic Keybindings:");
421         System.out.println("\tC-l - clear screen");        
422         System.out.println("\tC-a - beginning of line");
423         System.out.println("\tC-e - end of line");
424         System.out.println("\tC-b - backward character (left arrow also works)");
425         System.out.println("\tM-b - backward word");
426         System.out.println("\tC-f - forward character (right arrow also works)");
427         System.out.println("\tM-f - forward word");
428         System.out.println("\tC-d - delete character under cursor");
429         System.out.println("\tM-d - delete word forward");
430         System.out.println("\tM-backspace - delete word backward");
431         System.out.println("\tC-k - delete from cursor to end of line");
432         System.out.println("\tC-u - delete entire line, regardless of cursor position\n");
433
434         System.out.println("Command History:");
435         System.out.println("\tC-r - search backward in history (repeating C-r continues the search)");
436         System.out.println("\tC-p - move backwards through history (up arrow also works)");
437         System.out.println("\tC-n - move forwards through history (down arrow also works)\n");
438
439     }
440
441     /**
442      * @param args
443      */
444     public static void main(String[] args) {
445         int rv = 0;
446         
447         try {
448             AAFSSO aafsso = new AAFSSO(args);
449             String noexit = aafsso.access().getProperty("no_exit");
450             try {
451                 PropAccess access = aafsso.access();
452
453                 if (aafsso.ok()) {
454                     Define.set(access);
455                     AuthzEnv env = new AuthzEnv(access);
456                     
457                     Reader rdr = null;
458                     boolean exitOnFailure = true;
459                     /*
460                      * Check for "-" options anywhere in command line
461                      */
462                     StringBuilder sb = new StringBuilder();
463                     for (int i = 0; i < args.length; ++i) {
464                         if ("-i".equalsIgnoreCase(args[i])) {
465                             rdr = new InputStreamReader(System.in);
466                             // } else if ("-o".equalsIgnoreCase(args[i])) {
467                             // // shall we do something different? Output stream is
468                             // already done...
469                         } else if ("-f".equalsIgnoreCase(args[i])) {
470                             if (args.length > i + 1) {
471                                 rdr = new FileReader(args[++i]);
472                             }
473                         } else if ("-a".equalsIgnoreCase(args[i])) {
474                             exitOnFailure = false;
475                         } else if ("-c".equalsIgnoreCase(args[i])) {
476                             isConsole = true;
477                         } else if ("-s".equalsIgnoreCase(args[i]) && args.length > i + 1) {
478                             access.setProperty(Cmd.STARTDATE, args[++i]);
479                         } else if ("-e".equalsIgnoreCase(args[i]) && args.length > i + 1) {
480                             access.setProperty(Cmd.ENDDATE, args[++i]);
481                         } else if ("-t".equalsIgnoreCase(args[i])) {
482                             isTest = true;
483                         } else if ("-d".equalsIgnoreCase(args[i])) {
484                             showDetails = true;
485                         } else if ("-n".equalsIgnoreCase(args[i])) {
486                             ignoreDelay = true;
487                         } else {
488                             if (sb.length() > 0) {
489                                 sb.append(' ');
490                             }
491                             sb.append(args[i]);
492                         }
493                     }
494                     
495                     AAFConHttp aafcon = new AAFConHttp(access);
496 //                    
497 //                    SecurityInfoC<?> si = aafcon.securityInfo();
498 //                    Locator<URI> loc;
499                     
500                     aafsso.setLogDefault();
501                     aafsso.setStdErrDefault();
502     
503                     // Note, with AAF Locator, this may not longer be necessary 3/2018 Jonathan
504                     if (!aafsso.loginOnly()) {
505 //                        try {
506 //                            loc = new AAFLocator(si,new URI(access.getProperty(Config.AAF_URL)));
507 //                        } catch (Throwable t) {
508 //                            aafsso.setStdErrDefault();
509 //                            throw t;
510 //                        } finally {
511 //                            // Other Access is done writing to StdOut and StdErr, reset Std out
512 //                            aafsso.setLogDefault();
513 //                        }
514     
515                         TIMEOUT = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
516 //                        HMangr hman = new HMangr(access, loc).readTimeout(TIMEOUT).apiVersion(Config.AAF_DEFAULT_API_VERSION);
517                         
518                         if (access.getProperty(Config.AAF_DEFAULT_REALM)==null) {
519                             access.setProperty(Config.AAF_DEFAULT_REALM, "people.osaaf.org");
520                             aafsso.addProp(Config.AAF_DEFAULT_REALM, "people.osaaf.org");
521                         }
522             
523                         AAFcli aafcli = new AAFcli(access,env, new OutputStreamWriter(System.out),  
524                                 aafcon.hman(), aafcon.securityInfo(), aafcon.securityInfo().defSS);
525 //                            new HBasicAuthSS(si,aafsso.user(), access.decrypt(aafsso.enc_pass(),false)));
526 //                        }
527                         if (!ignoreDelay) {
528                             File delay = new File("aafcli.delay");
529                             if (delay.exists()) {
530                                 BufferedReader br = new BufferedReader(new FileReader(delay));
531                                 try {
532                                     globalDelay = Integer.parseInt(br.readLine());
533                                 } catch (Exception e) {
534                                     access.log(Level.DEBUG,e);
535                                 } finally {
536                                     br.close();
537                                 }
538                             }
539                         }
540                         try {
541                             if (isConsole) {
542                                 System.out.println("Type 'help' for short help or 'help -d' for detailed help with aafcli commands");
543                                 System.out.println("Type '?' for help with command line editing");
544                                 System.out.println("Type 'q', 'quit', or 'exit' to quit aafcli\n");
545             
546                                 ConsoleReader reader = new ConsoleReader();
547                                 try {
548                                     reader.setPrompt("aafcli > ");
549                 
550                                     String line;
551                                     while ((line = reader.readLine()) != null) {
552                                         showDetails = (line.contains("-d"));
553                 
554                                         if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("q") || line.equalsIgnoreCase("exit")) {
555                                             break;
556                                         } else if (line.equalsIgnoreCase("--help -d") || line.equalsIgnoreCase("help -d") 
557                                                 || line.equalsIgnoreCase("help")) {
558                                             line = "--help";
559                                         } else if (line.equalsIgnoreCase("cls")) {
560                                             reader.clearScreen();
561                                             continue;
562                                         } else if (line.equalsIgnoreCase("?")) {
563                                             keyboardHelp();
564                                             continue;
565                                         }
566                                         try {
567                                             aafcli.eval(line);
568                                             pw.flush();
569                                         } catch (Exception e) {
570                                             pw.println(e.getMessage());
571                                             pw.flush();
572                                         }
573                                     }
574                                 } finally {
575                                     reader.close();
576                                 }
577                             } else if (rdr != null) {
578                                 BufferedReader br = new BufferedReader(rdr);
579                                 try {
580                                     String line;
581                                     while ((line = br.readLine()) != null) {
582                                         if (!aafcli.eval(line) && exitOnFailure) {
583                                             rv = 1;
584                                             break;
585                                         }
586                                     }
587                                 } finally {
588                                     br.close();
589                                 }
590                             } else { // just run the command line
591                                 aafcli.verbose(false);
592                                 if (sb.length() == 0) {
593                                     sb.append("--help");
594                                 }
595                                 rv = aafcli.eval(sb.toString()) ? 0 : 1;
596                             }
597                             
598                         } finally {
599                             aafcli.close();
600             
601                             // Don't close if No Reader, or it's a Reader of Standard In
602                             if (rdr != null && !(rdr instanceof InputStreamReader)) {
603                                 rdr.close();
604                             }
605                         }
606                     }
607                 }
608             } finally {
609                 aafsso.close();
610                 StringBuilder err = aafsso.err();
611                 if (err != null) {
612                     err.append("to continue...");
613                     System.err.println(err);
614                 }
615             }
616             if (noexit==null) {
617                 return;
618             }
619
620
621         } catch (MessageException e) {
622             System.out.println("MessageException caught");
623
624             System.err.println(e.getMessage());
625         } catch (Exception e) {
626             e.printStackTrace(System.err);
627         }
628         System.exit(rv);
629     }
630
631     public boolean isTest() {
632         return AAFcli.isTest;
633     }
634     
635     public boolean isDetailed() {
636         return AAFcli.showDetails;
637     }
638
639     public String typeString(Class<?> cls, boolean json) {
640         return "application/" + cls.getSimpleName() + "+" + (json ? "json" : "xml");//+ ";version=" + hman.apiVersion();
641     }
642
643     public String forceString() {
644         return force;
645     }
646
647     public boolean addRequest() {
648         return request;
649     }
650
651     public void clearSingleLineProperties() {
652         force  = null;
653         request = false;
654         showDetails = false;
655     }
656
657     public void gui(boolean b) {
658         gui  = b;
659     }
660
661 }