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