51986b923943bdd75c54bb771b13595ea1b4add3
[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 {
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++)&&(!"nothing".equals(largs[idx]))) {
219                             for (String str : largs[idx].split(",")) {
220                                 try {
221                                     if ("Exception".equalsIgnoreCase(str)) {
222                                         expect.add(-1);
223                                     } else {
224                                         expect.add(Integer.parseInt(str));
225                                     }
226                                 } catch (NumberFormatException e) {
227                                     throw new CadiException("\"expect\" should be followed by Number");
228                                 }
229                             }
230                         ++idx;
231                 }
232                     continue;
233                     // Sleep, typically for reports, to allow DB to update
234                     // Milliseconds
235                     
236                 } else if ("sleep".equalsIgnoreCase(largs[idx])) {
237                     Integer t = Integer.parseInt(largs[++idx]);
238                     pw.println("sleep " + t);
239                     Thread.sleep(t);
240                     ++idx;
241                     continue;
242                 } else if ("delay".equalsIgnoreCase(largs[idx])) {
243                     delay = Integer.parseInt(largs[++idx]);
244                     pw.println("delay " + delay);
245                     ++idx;
246                     continue;
247                 } else if ("pause".equalsIgnoreCase(largs[idx])) {
248                     pw.println("Press <Return> to continue...");
249                     ++idx;
250                     // Sonar insists we do something with the string, though it's only a pause.  Not very helpful...
251                     BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
252                     String sonar;
253                     try {
254                         sonar = br.readLine();
255                     } finally {
256                         br.close();
257                     }
258                     sonar=""; // this useless code brought to you by Sonar.
259                     pw.print(sonar);
260                     continue;
261                 } else if ("exit".equalsIgnoreCase(largs[idx])) {
262                     pw.println("Exiting...");
263                     return false;
264                 } else if ("set".equalsIgnoreCase(largs[idx])) {
265                     while (largs.length > ++idx) {
266                         int equals = largs[idx].indexOf('=');
267                         String tag, value;
268                         if (equals < 0) {
269                             tag = largs[idx];
270                             value = access.getProperty(Config.AAF_APPPASS,null);
271                             if (value==null) {
272                                 break;
273                             } else {
274                                 value = access.decrypt(value, false);
275                                 if (value==null) {
276                                     break;
277                                 }
278                                 access.getProperties().put(tag, value);
279                                 pw.println("set " + tag + " <encrypted>");
280                             }
281                         } else {
282                             tag = largs[idx].substring(0, equals);
283                             value = largs[idx].substring(++equals);
284                             pw.println("set " + tag + ' ' + value);
285                         }
286                         boolean isTrue = "TRUE".equalsIgnoreCase(value);
287                         if ("FORCE".equalsIgnoreCase(tag)) {
288                             force = value;
289                         } else if ("REQUEST".equalsIgnoreCase(tag)) {
290                             request = isTrue;
291                         } else if ("DETAILS".equalsIgnoreCase(tag)) {
292                             showDetails = isTrue;
293                         } else {
294                             access.getProperties().put(tag, value);
295                         }
296                     }
297                     continue;
298                     // Allow Script to indicate if Failure is what is expected
299                 }
300
301             } 
302             
303             if ("REQUEST".equalsIgnoreCase(largs[idx])) {
304                 request=true;
305                 ++idx;
306             } else if ("FORCE".equalsIgnoreCase(largs[idx])) {
307                 force="true";
308                 ++idx;
309             } else if ("DETAILS".equalsIgnoreCase(largs[idx])) {
310                 showDetails=true;
311                 ++idx;
312             }
313
314             int ret = 0;
315             for (Cmd c : cmds) {
316                 if (largs[idx].equalsIgnoreCase(c.getName())) {
317                     if (verbose) {
318                         pw.println(line);
319                         if (expect.size() > 0) {
320                             pw.print("** Expect ");
321                             boolean first = true;
322                             for (Integer i : expect) {
323                                 if (first) {
324                                     first = false;
325                                 } else {
326                                     pw.print(',');
327                                 }
328                                 pw.print(i);
329                             }
330                             pw.println(" **");
331                         }
332                     }
333                     try {
334                         ret = c.exec(++idx, largs);
335                         if (delay+globalDelay > 0) {
336                             Thread.sleep((long)(delay+globalDelay));
337                         }
338                     } catch (Exception e) {
339                         if (expect.contains(-1)) {
340                             pw.println(e.getMessage());
341                             ret = -1;
342                         } else {
343                             throw e;
344                         }
345                     } finally {
346                         clearSingleLineProperties();
347                     }
348                     rv = expect.isEmpty() || expect.contains(ret);
349                     if (verbose) {
350                         if (rv) {
351                             pw.println();
352                         } else {
353                             pw.print("!!! Unexpected Return Code: ");
354                             pw.print(ret);
355                             pw.println(", VALIDATE OUTPUT!!!");
356                         }
357                     }
358                     return rv;
359                 }
360             }
361             pw.write("Unknown Instruction \"");
362             pw.write(largs[idx]);
363             pw.write("\"\n");
364             idx = largs.length;// always end after one command
365         }
366         return rv;
367     }
368
369     private String[] argEval(String line) {
370         StringBuilder sb = new StringBuilder();
371         ArrayList<String> arr = new ArrayList<>();
372         boolean start = true;
373         char quote = 0;
374         char last = 0;
375         for (int i = 0; i < line.length(); ++i) {
376             char ch;
377             ch = line.charAt(i);
378             if (Character.isWhitespace(ch)) {
379                 if (start || last==',') {
380                     continue; // trim
381                 } else if (quote != 0) {
382                     sb.append(ch);
383                 } else {
384                     arr.add(sb.toString());
385                     sb.setLength(0);
386                     start = true;
387                 }
388             } else if (ch == '\'' || ch == '"') { // toggle
389                 if (quote == ch) {
390                     quote = 0;
391                 } else {
392                     quote = ch;
393                 }
394             } else if (ch=='|' && quote==0) {
395                 arr.add(sb.toString());
396                 sb.setLength(0);
397                 start = true;
398             } else {
399                 start = false;
400                 sb.append(ch);
401                 last = ch;
402             }
403         }
404         if (sb.length() > 0) {
405             arr.add(sb.toString());
406         }
407
408         String[] rv = new String[arr.size()];
409         arr.toArray(rv);
410         return rv;
411     }
412
413     public static void keyboardHelp() {
414         System.out.println("'C-' means hold the ctrl key down while pressing the next key.");
415         System.out.println("'M-' means hold the alt key down while pressing the next key.");
416         System.out.println("For instance, C-b means hold ctrl key and press b, M-b means hold alt and press b\n");
417
418         System.out.println("Basic Keybindings:");
419         System.out.println("\tC-l - clear screen");        
420         System.out.println("\tC-a - beginning of line");
421         System.out.println("\tC-e - end of line");
422         System.out.println("\tC-b - backward character (left arrow also works)");
423         System.out.println("\tM-b - backward word");
424         System.out.println("\tC-f - forward character (right arrow also works)");
425         System.out.println("\tM-f - forward word");
426         System.out.println("\tC-d - delete character under cursor");
427         System.out.println("\tM-d - delete word forward");
428         System.out.println("\tM-backspace - delete word backward");
429         System.out.println("\tC-k - delete from cursor to end of line");
430         System.out.println("\tC-u - delete entire line, regardless of cursor position\n");
431
432         System.out.println("Command History:");
433         System.out.println("\tC-r - search backward in history (repeating C-r continues the search)");
434         System.out.println("\tC-p - move backwards through history (up arrow also works)");
435         System.out.println("\tC-n - move forwards through history (down arrow also works)\n");
436
437     }
438
439     /**
440      * @param args
441      */
442     public static void main(String[] args) {
443         int rv = 0;
444         
445         try {
446             AAFSSO aafsso = new AAFSSO(args);
447             String noexit = aafsso.access().getProperty("no_exit");
448             try {
449                 PropAccess access = aafsso.access();
450
451                 if (aafsso.ok()) {
452                     Define.set(access);
453                     AuthzEnv env = new AuthzEnv(access);
454                     
455                     Reader rdr = null;
456                     boolean exitOnFailure = true;
457                     /*
458                      * Check for "-" options anywhere in command line
459                      */
460                     StringBuilder sb = new StringBuilder();
461                     for (int i = 0; i < args.length; ++i) {
462                         if ("-i".equalsIgnoreCase(args[i])) {
463                             rdr = new InputStreamReader(System.in);
464                             // } else if ("-o".equalsIgnoreCase(args[i])) {
465                             // // shall we do something different? Output stream is
466                             // already done...
467                         } else if ("-f".equalsIgnoreCase(args[i])) {
468                             if (args.length > i + 1) {
469                                 rdr = new FileReader(args[++i]);
470                             }
471                         } else if ("-a".equalsIgnoreCase(args[i])) {
472                             exitOnFailure = false;
473                         } else if ("-c".equalsIgnoreCase(args[i])) {
474                             isConsole = true;
475                         } else if ("-s".equalsIgnoreCase(args[i]) && args.length > i + 1) {
476                             access.setProperty(Cmd.STARTDATE, args[++i]);
477                         } else if ("-e".equalsIgnoreCase(args[i]) && args.length > i + 1) {
478                             access.setProperty(Cmd.ENDDATE, args[++i]);
479                         } else if ("-t".equalsIgnoreCase(args[i])) {
480                             isTest = true;
481                         } else if ("-d".equalsIgnoreCase(args[i])) {
482                             showDetails = true;
483                         } else if ("-n".equalsIgnoreCase(args[i])) {
484                             ignoreDelay = true;
485                         } else {
486                             if (sb.length() > 0) {
487                                 sb.append(' ');
488                             }
489                             sb.append(args[i]);
490                         }
491                     }
492                     
493                     AAFConHttp aafcon = new AAFConHttp(access);
494 //                    
495 //                    SecurityInfoC<?> si = aafcon.securityInfo();
496 //                    Locator<URI> loc;
497                     
498                     aafsso.setLogDefault();
499                     aafsso.setStdErrDefault();
500     
501                     // Note, with AAF Locator, this may not longer be necessary 3/2018 Jonathan
502                     if (!aafsso.loginOnly()) {
503 //                        try {
504 //                            loc = new AAFLocator(si,new URI(access.getProperty(Config.AAF_URL)));
505 //                        } catch (Throwable t) {
506 //                            aafsso.setStdErrDefault();
507 //                            throw t;
508 //                        } finally {
509 //                            // Other Access is done writing to StdOut and StdErr, reset Std out
510 //                            aafsso.setLogDefault();
511 //                        }
512     
513                         TIMEOUT = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
514 //                        HMangr hman = new HMangr(access, loc).readTimeout(TIMEOUT).apiVersion(Config.AAF_DEFAULT_API_VERSION);
515                         
516                         if (access.getProperty(Config.AAF_DEFAULT_REALM)==null) {
517                             access.setProperty(Config.AAF_DEFAULT_REALM, "people.osaaf.org");
518                             aafsso.addProp(Config.AAF_DEFAULT_REALM, "people.osaaf.org");
519                         }
520             
521                         AAFcli aafcli = new AAFcli(access,env, new OutputStreamWriter(System.out),  
522                                 aafcon.hman(), aafcon.securityInfo(), aafcon.securityInfo().defSS);
523 //                            new HBasicAuthSS(si,aafsso.user(), access.decrypt(aafsso.enc_pass(),false)));
524 //                        }
525                         if (!ignoreDelay) {
526                             File delay = new File("aafcli.delay");
527                             if (delay.exists()) {
528                                 BufferedReader br = new BufferedReader(new FileReader(delay));
529                                 try {
530                                     globalDelay = Integer.parseInt(br.readLine());
531                                 } catch (Exception e) {
532                                     access.log(Level.DEBUG,e);
533                                 } finally {
534                                     br.close();
535                                 }
536                             }
537                         }
538                         try {
539                             if (isConsole) {
540                                 System.out.println("Type 'help' for short help or 'help -d' for detailed help with aafcli commands");
541                                 System.out.println("Type '?' for help with command line editing");
542                                 System.out.println("Type 'q', 'quit', or 'exit' to quit aafcli\n");
543             
544                                 ConsoleReader reader = new ConsoleReader();
545                                 try {
546                                     reader.setPrompt("aafcli > ");
547                 
548                                     String line;
549                                     while ((line = reader.readLine()) != null) {
550                                         showDetails = (line.contains("-d"));
551                 
552                                         if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("q") || line.equalsIgnoreCase("exit")) {
553                                             break;
554                                         } else if (line.equalsIgnoreCase("--help -d") || line.equalsIgnoreCase("help -d") 
555                                                 || line.equalsIgnoreCase("help")) {
556                                             line = "--help";
557                                         } else if (line.equalsIgnoreCase("cls")) {
558                                             reader.clearScreen();
559                                             continue;
560                                         } else if (line.equalsIgnoreCase("?")) {
561                                             keyboardHelp();
562                                             continue;
563                                         }
564                                         try {
565                                             aafcli.eval(line);
566                                             pw.flush();
567                                         } catch (Exception e) {
568                                             pw.println(e.getMessage());
569                                             pw.flush();
570                                         }
571                                     }
572                                 } finally {
573                                     reader.close();
574                                 }
575                             } else if (rdr != null) {
576                                 BufferedReader br = new BufferedReader(rdr);
577                                 try {
578                                     String line;
579                                     while ((line = br.readLine()) != null) {
580                                         if (!aafcli.eval(line) && exitOnFailure) {
581                                             rv = 1;
582                                             break;
583                                         }
584                                     }
585                                 } finally {
586                                     br.close();
587                                 }
588                             } else { // just run the command line
589                                 aafcli.verbose(false);
590                                 if (sb.length() == 0) {
591                                     sb.append("--help");
592                                 }
593                                 rv = aafcli.eval(sb.toString()) ? 0 : 1;
594                             }
595                             
596                         } finally {
597                             aafcli.close();
598             
599                             // Don't close if No Reader, or it's a Reader of Standard In
600                             if (rdr != null && !(rdr instanceof InputStreamReader)) {
601                                 rdr.close();
602                             }
603                         }
604                     }
605                 }
606             } finally {
607                 aafsso.close();
608                 StringBuilder err = aafsso.err();
609                 if (err != null) {
610                     err.append("to continue...");
611                     System.err.println(err);
612                 }
613             }
614             if (noexit==null) {
615                 return;
616             }
617
618
619         } catch (MessageException e) {
620             System.out.println("MessageException caught");
621
622             System.err.println(e.getMessage());
623         } catch (Exception e) {
624             e.printStackTrace(System.err);
625         }
626         System.exit(rv);
627     }
628
629     public boolean isTest() {
630         return AAFcli.isTest;
631     }
632     
633     public boolean isDetailed() {
634         return AAFcli.showDetails;
635     }
636
637     public String typeString(Class<?> cls, boolean json) {
638         return "application/" + cls.getSimpleName() + "+" + (json ? "json" : "xml");//+ ";version=" + hman.apiVersion();
639     }
640
641     public String forceString() {
642         return force;
643     }
644
645     public boolean addRequest() {
646         return request;
647     }
648
649     public void clearSingleLineProperties() {
650         force  = null;
651         request = false;
652         showDetails = false;
653     }
654
655     public void gui(boolean b) {
656         gui  = b;
657     }
658
659 }