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