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