72aa0ccd62760b9ce93443589324119da0b8b2d0
[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.net.URI;
34 import java.util.ArrayList;
35 import java.util.List;
36
37 import org.onap.aaf.auth.cmd.mgmt.Mgmt;
38 import org.onap.aaf.auth.cmd.ns.NS;
39 import org.onap.aaf.auth.cmd.perm.Perm;
40 import org.onap.aaf.auth.cmd.role.Role;
41 import org.onap.aaf.auth.cmd.user.User;
42 import org.onap.aaf.auth.common.Define;
43 import org.onap.aaf.auth.env.AuthzEnv;
44 import org.onap.aaf.cadi.Access;
45 import org.onap.aaf.cadi.CadiException;
46 import org.onap.aaf.cadi.Locator;
47 import org.onap.aaf.cadi.PropAccess;
48 import org.onap.aaf.cadi.SecuritySetter;
49 import org.onap.aaf.cadi.Access.Level;
50 import org.onap.aaf.cadi.aaf.v2_0.AAFLocator;
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         private static final String HTTPS = "https://";
63         protected static PrintWriter pw;
64         protected HMangr hman;
65         // Storage for last reused client. We can do this
66         // because we're technically "single" threaded calls.
67         public Retryable<?> prevCall;
68
69         protected SecuritySetter<HttpURLConnection> ss;
70 //      protected AuthzEnv env;
71         private boolean close;
72         private List<Cmd> cmds;
73
74         // Lex State
75         private ArrayList<Integer> expect = new ArrayList<Integer>();
76         private boolean verbose = true;
77         private int delay;
78         private SecurityInfoC<HttpURLConnection> si;
79         private boolean request = false;
80         private String force = null;
81         private boolean gui = false;
82         // Package on purpose
83         Access access;
84         AuthzEnv env;
85
86         private static int TIMEOUT = Integer.parseInt(Config.AAF_CONN_TIMEOUT_DEF);
87         private static boolean isConsole = false;
88         private static boolean isTest = false;
89         private static boolean showDetails = false;
90         private static boolean ignoreDelay = false;
91         private static int globalDelay=0;
92         
93         public static int timeout() {
94                 return TIMEOUT;
95         }
96
97         // Create when only have Access
98         public AAFcli(Access access, Writer wtr, HMangr hman, SecurityInfoC<HttpURLConnection> si, SecuritySetter<HttpURLConnection> ss) throws APIException {
99                 this(access,new AuthzEnv(access.getProperties()),wtr,hman, si,ss);
100         }
101
102         public AAFcli(Access access, AuthzEnv env, Writer wtr, HMangr hman, SecurityInfoC<HttpURLConnection> si, SecuritySetter<HttpURLConnection> ss) throws APIException {
103                 this.env = env;
104                 this.access = access;
105                 this.ss = ss;
106                 this.hman = hman;
107                 this.si = si;
108                 if (wtr instanceof PrintWriter) {
109                         pw = (PrintWriter) wtr;
110                         close = false;
111                 } else {
112                         pw = new PrintWriter(wtr);
113                         close = true;
114                 }
115
116
117                 /*
118                  * Create Cmd Tree
119                  */
120                 cmds = new ArrayList<Cmd>();
121
122                 Role role = new Role(this);
123                 cmds.add(new Help(this, cmds));
124                 cmds.add(new Version(this));
125                 cmds.add(new Perm(role));
126                 cmds.add(role);
127                 cmds.add(new User(this));
128                 cmds.add(new NS(this));
129                 cmds.add(new Mgmt(this));
130         }
131
132         public void verbose(boolean v) {
133                 verbose = v;
134         }
135
136         public void close() {
137                 if (hman != null) {
138                         hman.close();
139                         hman = null;
140                 }
141                 if (close) {
142                         pw.close();
143                 }
144         }
145
146         public boolean eval(String line) throws Exception {
147                 if (line.length() == 0) {
148                         return true;
149                 } else if (line.startsWith("#")) {
150                         pw.println(line);
151                         return true;
152                 }
153
154                 String[] largs = argEval(line);
155                 int idx = 0;
156
157                 // Variable replacement
158                 StringBuilder sb = null;
159                 while (idx < largs.length) {
160                         int e = 0;
161                         for (int v = largs[idx].indexOf("@["); v >= 0; v = largs[idx].indexOf("@[", v + 1)) {
162                                 if (sb == null) {
163                                         sb = new StringBuilder();
164                                 }
165                                 sb.append(largs[idx], e, v);
166                                 if ((e = largs[idx].indexOf(']', v)) >= 0) {
167                                         String p = access.getProperty(largs[idx].substring(v + 2, e),null);
168                                         if(p==null) {
169                                                 p = System.getProperty(largs[idx].substring(v+2,e));
170                                         }
171                                         ++e;
172                                         if (p != null) {
173                                                 sb.append(p);
174                                         }
175                                 }
176                         }
177                         if (sb != null && sb.length() > 0) {
178                                 sb.append(largs[idx], e, largs[idx].length());
179                                 largs[idx] = sb.toString();
180                                 sb.setLength(0);
181                         }
182                         ++idx;
183                 }
184
185                 idx = 0;
186                 boolean rv = true;
187                 while (rv && idx < largs.length) {
188                         // Allow Script to change Credential
189                         if (!gui) {
190                                 if("as".equalsIgnoreCase(largs[idx])) {
191                                         if (largs.length > ++idx) {
192                                                 // get Password from Props with ID as Key
193                                                 String user = largs[idx++];
194                                                 int colon = user.indexOf(':');
195                                                 String pass;
196                                                 if (colon > 0) {
197                                                         pass = user.substring(colon + 1);
198                                                         user = user.substring(0, colon);
199                                                 } else {
200                                                         pass = access.getProperty(user, null);
201                                                 }
202                                                 if (pass != null) {
203                                                         pass = access.decrypt(pass, false);
204                                                         access.getProperties().put(user, pass);
205                                                         ss = new HBasicAuthSS(si, user, pass);
206                                                         pw.println("as " + user);
207                                                 } else { // get Pass from System Properties, under name of
208                                                         // Tag
209                                                         pw.println("ERROR: No password set for " + user);
210                                                         rv = false;
211                                                 }
212                                                 continue;
213                                         }
214                                 } else if ("expect".equalsIgnoreCase(largs[idx])) {
215                                         expect.clear();
216                                         if (largs.length > idx++) {
217                                                 if (!"nothing".equals(largs[idx])) {
218                                                         for (String str : largs[idx].split(",")) {
219                                                                 try {
220                                                                         if ("Exception".equalsIgnoreCase(str)) {
221                                                                                 expect.add(-1);
222                                                                         } else {
223                                                                                 expect.add(Integer.parseInt(str));
224                                                                         }
225                                                                 } catch (NumberFormatException e) {
226                                                                         throw new CadiException("\"expect\" should be followed by Number");
227                                                                 }
228                                                         }
229                                                 ++idx;
230                                                 }
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                                         String sonar = new BufferedReader(new InputStreamReader(System.in)).readLine();
252                                         sonar=""; // this useless code brought to you by Sonar.
253                                         pw.print(sonar);
254                                         continue;
255                                 } else if ("exit".equalsIgnoreCase(largs[idx])) {
256                                         pw.println("Exiting...");
257                                         return false;
258                                 }
259
260                         } 
261                         
262                         if("REQUEST".equalsIgnoreCase(largs[idx])) {
263                                 request=true;
264                                 ++idx;
265                         } else if("FORCE".equalsIgnoreCase(largs[idx])) {
266                                 force="true";
267                                 ++idx;
268                         } else if("DETAILS".equalsIgnoreCase(largs[idx])) {
269                                 showDetails=true;
270                                 ++idx;
271                         } else if ("set".equalsIgnoreCase(largs[idx])) {
272                                 while (largs.length > ++idx) {
273                                         int equals = largs[idx].indexOf('=');
274                                         String tag, value;
275                                         if (equals < 0) {
276                                                 tag = largs[idx];
277                                                 value = access.getProperty(Config.AAF_APPPASS,null);
278                                                 if(value==null) {
279                                                         break;
280                                                 } else {
281                                                         value = access.decrypt(value, false);
282                                                         if(value==null) {
283                                                                 break;
284                                                         }
285                                                         access.getProperties().put(tag, value);
286                                                         pw.println("set " + tag + " <encrypted>");
287                                                 }
288                                         } else {
289                                                 tag = largs[idx].substring(0, equals);
290                                                 value = largs[idx].substring(++equals);
291                                                 pw.println("set " + tag + ' ' + value);
292                                         }
293                                         boolean isTrue = "TRUE".equalsIgnoreCase(value);
294                                         if("FORCE".equalsIgnoreCase(tag)) {
295                                                 force = value;
296                                         } else if("REQUEST".equalsIgnoreCase(tag)) {
297                                                 request = isTrue;
298                                         } else if("DETAILS".equalsIgnoreCase(tag)) {
299                                                 showDetails = isTrue;
300                                         } else {
301                                                 access.getProperties().put(tag, value);
302                                         }
303                                 }
304                                 continue;
305                                 // Allow Script to indicate if Failure is what is expected
306                         }
307
308                         int ret = 0;
309                         for (Cmd c : cmds) {
310                                 if (largs[idx].equalsIgnoreCase(c.getName())) {
311                                         if (verbose) {
312                                                 pw.println(line);
313                                                 if (expect.size() > 0) {
314                                                         pw.print("** Expect ");
315                                                         boolean first = true;
316                                                         for (Integer i : expect) {
317                                                                 if (first) {
318                                                                         first = false;
319                                                                 } else {
320                                                                         pw.print(',');
321                                                                 }
322                                                                 pw.print(i);
323                                                         }
324                                                         pw.println(" **");
325                                                 }
326                                         }
327                                         try {
328                                                 ret = c.exec(++idx, largs);
329                                                 if (delay+globalDelay > 0) {
330                                                         Thread.sleep((long)(delay+globalDelay));
331                                                 }
332                                         } catch (Exception e) {
333                                                 if (expect.contains(-1)) {
334                                                         pw.println(e.getMessage());
335                                                         ret = -1;
336                                                 } else {
337                                                         throw e;
338                                                 }
339                                         } finally {
340                                                 clearSingleLineProperties();
341                                         }
342                                         rv = expect.isEmpty() ? true : expect.contains(ret);
343                                         if (verbose) {
344                                                 if (rv) {
345                                                         pw.println();
346                                                 } else {
347                                                         pw.print("!!! Unexpected Return Code: ");
348                                                         pw.print(ret);
349                                                         pw.println(", VALIDATE OUTPUT!!!");
350                                                 }
351                                         }
352                                         return rv;
353                                 }
354                         }
355                         pw.write("Unknown Instruction \"");
356                         pw.write(largs[idx]);
357                         pw.write("\"\n");
358                         idx = largs.length;// always end after one command
359                 }
360                 return rv;
361         }
362
363         private String[] argEval(String line) {
364                 StringBuilder sb = new StringBuilder();
365                 ArrayList<String> arr = new ArrayList<String>();
366                 boolean start = true;
367                 char quote = 0;
368                 char last = 0;
369                 for (int i = 0; i < line.length(); ++i) {
370                         char ch;
371                         if (Character.isWhitespace(ch = line.charAt(i))) {
372                                 if (start || last==',') {
373                                         continue; // trim
374                                 } else if (quote != 0) {
375                                         sb.append(ch);
376                                 } else {
377                                         arr.add(sb.toString());
378                                         sb.setLength(0);
379                                         start = true;
380                                 }
381                         } else if (ch == '\'' || ch == '"') { // toggle
382                                 if (quote == ch) {
383                                         quote = 0;
384                                 } else {
385                                         quote = ch;
386                                 }
387                         } else if(ch=='|' && quote==0) {
388                                 arr.add(sb.toString());
389                                 sb.setLength(0);
390                                 start = true;
391                         } else {
392                                 start = false;
393                                 sb.append(ch);
394                                 last = ch;
395                         }
396                 }
397                 if (sb.length() > 0) {
398                         arr.add(sb.toString());
399                 }
400
401                 String[] rv = new String[arr.size()];
402                 arr.toArray(rv);
403                 return rv;
404         }
405
406         public static void keyboardHelp() {
407                 System.out.println("'C-' means hold the ctrl key down while pressing the next key.");
408                 System.out.println("'M-' means hold the alt key down while pressing the next key.");
409                 System.out.println("For instance, C-b means hold ctrl key and press b, M-b means hold alt and press b\n");
410
411                 System.out.println("Basic Keybindings:");
412                 System.out.println("\tC-l - clear screen");        
413                 System.out.println("\tC-a - beginning of line");
414                 System.out.println("\tC-e - end of line");
415                 System.out.println("\tC-b - backward character (left arrow also works)");
416                 System.out.println("\tM-b - backward word");
417                 System.out.println("\tC-f - forward character (right arrow also works)");
418                 System.out.println("\tM-f - forward word");
419                 System.out.println("\tC-d - delete character under cursor");
420                 System.out.println("\tM-d - delete word forward");
421                 System.out.println("\tM-backspace - delete word backward");
422                 System.out.println("\tC-k - delete from cursor to end of line");
423                 System.out.println("\tC-u - delete entire line, regardless of cursor position\n");
424
425                 System.out.println("Command History:");
426                 System.out.println("\tC-r - search backward in history (repeating C-r continues the search)");
427                 System.out.println("\tC-p - move backwards through history (up arrow also works)");
428                 System.out.println("\tC-n - move forwards through history (down arrow also works)\n");
429
430         }
431
432         /**
433          * @param args
434          */
435         public static void main(String[] args) {
436                 int rv = 0;
437                 
438                 try {
439                         AAFSSO aafsso = new AAFSSO(args);
440                         try {
441                                 PropAccess access = aafsso.access();
442                                 Define.set(access);
443                                 AuthzEnv env = new AuthzEnv(access);
444                                 
445                                 StringBuilder err = aafsso.err();
446                                 String noexit = access.getProperty("no_exit");
447                                 if (err != null) {
448                                         err.append("to continue...");
449                                         System.err.println(err);
450                                         if(noexit!=null) {
451                                                 System.exit(1);
452                                         }
453                                 }
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                                 SecurityInfoC<HttpURLConnection> si = SecurityInfoC.instance(access, HttpURLConnection.class);
494                                 Locator<URI> loc;
495                                 String aafUrl = access.getProperty(Config.AAF_URL);
496                                 if(aafUrl==null) {
497                                         aafsso.setLogDefault();
498                                         aafsso.setStdErrDefault();
499                                         aafUrl=AAFSSO.cons.readLine("aaf_url=%s", HTTPS);
500                                         if(aafUrl.length()==0) {
501                                                 System.exit(0);
502                                         } else if(!aafUrl.startsWith(HTTPS)) {
503                                                 aafUrl=HTTPS+aafUrl;
504                                         }
505                                         aafsso.addProp(Config.AAF_URL, aafUrl);
506                                 } 
507                                 // Note, with AAF Locator, this may not longer be necessary 3/2018 Jonathan
508                                 if(!aafsso.loginOnly()) {
509                                         try {
510                                                 loc = new AAFLocator(si,new URI(aafUrl));
511                                         } catch (Throwable t) {
512                                                 aafsso.setStdErrDefault();
513                                                 throw t;
514                                         } finally {
515                                                 // Other Access is done writing to StdOut and StdErr, reset Std out
516                                                 aafsso.setLogDefault();
517                                         }
518
519                                         TIMEOUT = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF));
520                                         HMangr hman = new HMangr(access, loc).readTimeout(TIMEOUT).apiVersion("2.0");
521                                         
522                                         if(access.getProperty(Config.AAF_DEFAULT_REALM)==null) {
523                                                 access.log(Level.ERROR, Config.AAF_DEFAULT_REALM,"is required");
524                                         }
525                 
526                                         
527                                         AAFcli aafcli = new AAFcli(access,env, new OutputStreamWriter(System.out), hman, si, 
528                                                 new HBasicAuthSS(si,aafsso.user(), access.decrypt(aafsso.enc_pass(),false)));
529                                         if(!ignoreDelay) {
530                                                 File delay = new File("aafcli.delay");
531                                                 if(delay.exists()) {
532                                                         BufferedReader br = new BufferedReader(new FileReader(delay));
533                                                         try {
534                                                                 globalDelay = Integer.parseInt(br.readLine());
535                                                         } catch(Exception e) {
536                                                                 access.log(Level.DEBUG,e);
537                                                         } finally {
538                                                                 br.close();
539                                                         }
540                                                 }
541                                         }
542                                         try {
543                                                 if (isConsole) {
544                                                         System.out.println("Type 'help' for short help or 'help -d' for detailed help with aafcli commands");
545                                                         System.out.println("Type '?' for help with command line editing");
546                                                         System.out.println("Type 'q', 'quit', or 'exit' to quit aafcli\n");
547                 
548                                                         ConsoleReader reader = new ConsoleReader();
549                                                         try {
550                                                                 reader.setPrompt("aafcli > ");
551                         
552                                                                 String line;
553                                                                 while ((line = reader.readLine()) != null) {
554                                                                         showDetails = (line.contains("-d"))?true:false;
555                         
556                                                                         if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("q") || line.equalsIgnoreCase("exit")) {
557                                                                                 break;
558                                                                         } else if (line.equalsIgnoreCase("--help -d") || line.equalsIgnoreCase("help -d") 
559                                                                                         || line.equalsIgnoreCase("help")) {
560                                                                                 line = "--help";
561                                                                         } else if (line.equalsIgnoreCase("cls")) {
562                                                                                 reader.clearScreen();
563                                                                                 continue;
564                                                                         } else if (line.equalsIgnoreCase("?")) {
565                                                                                 keyboardHelp();
566                                                                                 continue;
567                                                                         }
568                                                                         try {
569                                                                                 aafcli.eval(line);
570                                                                                 pw.flush();
571                                                                         } catch (Exception e) {
572                                                                                 pw.println(e.getMessage());
573                                                                                 pw.flush();
574                                                                         }
575                                                                 }
576                                                         } finally {
577                                                                 reader.close();
578                                                         }
579                                                 } else if (rdr != null) {
580                                                         BufferedReader br = new BufferedReader(rdr);
581                                                         String line;
582                                                         while ((line = br.readLine()) != null) {
583                                                                 if (!aafcli.eval(line) && exitOnFailure) {
584                                                                         rv = 1;
585                                                                         break;
586                                                                 }
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                                 aafsso.writeFiles();
606                         } finally {
607                                 aafsso.close();
608                         }
609                         
610                 } catch (MessageException e) {
611                         System.out.println("MessageException caught");
612
613                         System.err.println(e.getMessage());
614                 } catch (Throwable 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 }