Remove Tabs, per Jococo
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / sso / AAFSSO.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.cadi.sso;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.PrintStream;
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.net.URISyntaxException;
33 import java.util.ArrayList;
34 import java.util.List;
35 import java.util.Map.Entry;
36 import java.util.Properties;
37
38 import org.onap.aaf.cadi.Access.Level;
39 import org.onap.aaf.cadi.CadiException;
40 import org.onap.aaf.cadi.LocatorException;
41 import org.onap.aaf.cadi.PropAccess;
42 import org.onap.aaf.cadi.Symm;
43 import org.onap.aaf.cadi.aaf.Defaults;
44 import org.onap.aaf.cadi.aaf.v2_0.AAFCon;
45 import org.onap.aaf.cadi.client.Future;
46 import org.onap.aaf.cadi.config.Config;
47 import org.onap.aaf.cadi.configure.ArtifactDir;
48 import org.onap.aaf.cadi.locator.SingleEndpointLocator;
49 import org.onap.aaf.cadi.util.MyConsole;
50 import org.onap.aaf.cadi.util.SubStandardConsole;
51 import org.onap.aaf.cadi.util.TheConsole;
52 import org.onap.aaf.misc.env.APIException;
53 import org.onap.aaf.misc.rosetta.env.RosettaDF;
54 import org.onap.aaf.misc.rosetta.env.RosettaEnv;
55
56 import locate.v1_1.Configuration;
57 import locate.v1_1.Configuration.Props;
58
59 public class AAFSSO {
60     public static final MyConsole  cons = TheConsole.implemented() ? new TheConsole() : new SubStandardConsole();
61 //    private static final int EIGHT_HOURS = 8 * 60 * 60 * 1000;
62
63     private Properties diskprops;
64     private boolean touchDiskprops;
65     private File dot_aaf = null;
66     private File sso = null; // instantiated, if ever, with diskprops
67
68     boolean removeSSO = false;
69     boolean loginOnly = false;
70     boolean doExit = true;
71     private PropAccess access;
72     private StringBuilder err;
73     private String user;
74     private String encrypted_pass;
75     private boolean use_X509;
76
77     private PrintStream os;
78
79     private Method close;
80     private final PrintStream stdOutOrig;
81     private final PrintStream stdErrOrig;
82     private boolean ok;
83
84     public AAFSSO(String[] args) throws IOException, CadiException {
85         this(args,new Properties());
86     }
87     
88     public AAFSSO(String[] args, ProcessArgs pa) throws IOException, CadiException {
89         this(args,pa.process(args, new Properties()));
90     }
91
92     public AAFSSO(String[] args, Properties dp) throws IOException, CadiException {
93         stdOutOrig = System.out;
94         stdErrOrig = System.err;
95         ok = true;
96         List<String> nargs = parseArgs(args);
97         diskprops = dp;
98         touchDiskprops = false;
99
100         dot_aaf = new File(System.getProperty("user.home") + "/.aaf");
101         if (!dot_aaf.exists()) {
102             dot_aaf.mkdirs();
103         }
104         File f = new File(dot_aaf, "sso.out");
105         os = new PrintStream(new FileOutputStream(f, true));
106         //System.setOut(os);
107         System.setErr(os);
108
109         sso = new File(dot_aaf, "sso.props");
110         if (sso.exists()) {
111             InputStream propStream = new FileInputStream(sso);
112             try {
113                 diskprops.load(propStream);
114             } finally {
115                 propStream.close();
116             }
117         }
118         
119         File dot_aaf_kf = new File(dot_aaf, "keyfile");
120
121         if (removeSSO) {
122             if (dot_aaf_kf.exists()) {
123                 dot_aaf_kf.setWritable(true, true);
124                 dot_aaf_kf.delete();
125             }
126             if (sso.exists()) {
127                 Properties temp = new Properties();
128                 // Keep only these
129                 for (Entry<Object, Object> es : diskprops.entrySet()) {
130                     if (Config.CADI_LATITUDE.equals(es.getKey()) ||
131                        Config.CADI_LONGITUDE.equals(es.getKey()) ||
132                        Config.AAF_DEFAULT_REALM.equals(es.getKey())) {
133                          temp.setProperty(es.getKey().toString(), es.getValue().toString());
134                     }
135                 }
136                 diskprops = temp;
137                 touchDiskprops = true;
138             }
139             String[] naargs = new String[nargs.size()];
140             nargs.toArray(naargs);
141             access = new PropAccess(os, naargs);
142             ok = false;
143             setLogDefault();
144             System.out.println("AAF SSO information removed");
145         } else {
146             //    Config.setDefaultRealm(access);
147     
148             if (!dot_aaf_kf.exists()) {
149                 // This will create, as required, or reuse
150                 ArtifactDir.getSymm(dot_aaf_kf);
151             }
152
153             for (Entry<Object, Object> es : diskprops.entrySet()) {
154                 nargs.add(es.getKey().toString() + '=' + es.getValue().toString());
155             }
156             String[] naargs = new String[nargs.size()];
157             nargs.toArray(naargs);
158             access = new PropAccess(os, naargs);
159             
160             if (loginOnly) {
161                 for (String tag : new String[] {Config.AAF_APPID, Config.AAF_APPPASS, 
162                         Config.CADI_ALIAS, Config.CADI_KEYSTORE,Config.CADI_KEYSTORE_PASSWORD,Config.CADI_KEY_PASSWORD}) {
163                     access.getProperties().remove(tag);
164                     diskprops.remove(tag);
165                 }
166                 touchDiskprops=true;
167 // TODO Do we want to require reset of Passwords at least every Eight Hours.
168 //            } else if (sso.lastModified() > (System.currentTimeMillis() - EIGHT_HOURS)) {
169 //                for (String tag : new String[] {Config.AAF_APPPASS,Config.CADI_KEYSTORE_PASSWORD,Config.CADI_KEY_PASSWORD}) {
170 //                    access.getProperties().remove(tag);
171 //                    diskprops.remove(tag);
172 //                }
173 //                touchDiskprops=true;
174             }
175     
176             String keyfile = access.getProperty(Config.CADI_KEYFILE); // in case its CertificateMan props
177             if (keyfile == null) {
178                 access.setProperty(Config.CADI_KEYFILE, dot_aaf_kf.getAbsolutePath());
179                 addProp(Config.CADI_KEYFILE,dot_aaf_kf.getAbsolutePath());
180             }
181     
182     
183             String alias, appID;
184             alias = access.getProperty(Config.CADI_ALIAS);
185             if (alias==null) {
186                 appID = access.getProperty(Config.AAF_APPID);
187                 user=appID;
188             } else {
189                 user=alias;
190                 appID=null;
191             }
192             
193             String aaf_container_ns = "";
194             if (appID!=null) {
195                 if( access.getProperty(Config.AAF_APPPASS)==null) {
196                     appID = user = cons.readLine("Deployer ID [%s]: ", user);
197                     access.setProperty(Config.AAF_APPID,appID);
198                     char[] password = cons.readPassword("Password for %s: ", user);
199                     if(password.length>0) {
200                         String app_pass = access.encrypt(new String(password));
201                            access.setProperty(Config.AAF_APPPASS,app_pass);
202                            diskprops.setProperty(Config.AAF_APPPASS,app_pass);
203                     }
204                     aaf_container_ns = cons.readLine("Container Namespace (blank if none)? [\"\"]: ", aaf_container_ns);
205                 }
206                  diskprops.setProperty(Config.AAF_APPID,appID);
207             }
208             
209             String keystore=access.getProperty(Config.CADI_KEYSTORE);
210             String keystore_pass=access.getProperty(Config.CADI_KEYSTORE_PASSWORD);
211             
212             if (user==null || (alias!=null && (keystore==null || keystore_pass==null))) {
213                 String select = null;
214                 String name;
215                 for (File tsf : dot_aaf.listFiles()) {
216                     name = tsf.getName();
217                     if (!name.contains("trust") && (name.endsWith(".jks") || name.endsWith(".p12"))) {
218                         setLogDefault();
219                         select = cons.readLine("Use %s for Identity? (y/n): ",tsf.getName());
220                         if ("y".equalsIgnoreCase(select)) {
221                             keystore = tsf.getCanonicalPath();
222                             access.setProperty(Config.CADI_KEYSTORE, keystore);
223                             addProp(Config.CADI_KEYSTORE, keystore);
224                             char[] password = cons.readPassword("Keystore Password: ");
225                             encrypted_pass= access.encrypt(new String(password));
226                             access.setProperty(Config.CADI_KEYSTORE_PASSWORD, encrypted_pass);
227                             addProp(Config.CADI_KEYSTORE_PASSWORD, encrypted_pass);
228                             
229                             // TODO READ Aliases out of Keystore?
230                             user = alias = cons.readLine("Keystore alias: ");
231                             access.setProperty(Config.CADI_ALIAS, user);
232                             addProp(Config.CADI_ALIAS, user);
233                             break;
234                         }
235                     }
236                 }
237                 if (alias==null) {
238                     user = appID = cons.readLine(Config.AAF_APPID + ": ");
239                     access.setProperty(Config.AAF_APPID, appID);
240                     addProp(Config.AAF_APPID, appID);
241                     char[] password = cons.readPassword(Config.AAF_APPPASS + ": ");
242                     encrypted_pass= access.encrypt(new String(password));
243                     access.setProperty(Config.AAF_APPPASS, encrypted_pass);
244                     addProp(Config.AAF_APPPASS, encrypted_pass);
245                 }
246             } else {
247                 encrypted_pass = access.getProperty(Config.CADI_KEYSTORE_PASSWORD);
248                 if (encrypted_pass == null) {
249                     keystore_pass = null;
250                     encrypted_pass = access.getProperty(Config.AAF_APPPASS);
251                 } else {
252                     keystore_pass = encrypted_pass;
253                 }
254             }
255             
256     
257             if (alias!=null) {
258                 use_X509 = true;
259             } else {
260                 use_X509 = false;
261                 Symm decryptor = ArtifactDir.getSymm(dot_aaf_kf);
262                 if (user == null) {
263                     if (sso.exists()) {
264                         String cm_url = access.getProperty(Config.AAF_URL_CM); // SSO might overwrite...
265                         FileInputStream fos = new FileInputStream(sso);
266                         try {
267                             access.load(fos);
268                             user = access.getProperty(Config.AAF_APPID);
269                             encrypted_pass = access.getProperty(Config.AAF_APPPASS);
270                             // decrypt with .aaf, and re-encrypt with regular Keyfile
271                             access.setProperty(Config.AAF_APPPASS,
272                                     access.encrypt(decryptor.depass(encrypted_pass)));
273                             if (cm_url != null) { //Command line CM_URL Overwrites ssofile.
274                                 access.setProperty(Config.AAF_URL_CM, cm_url);
275                             }
276                         } finally {
277                             fos.close();
278                         }
279                     } else {
280                         diskprops = new Properties();
281                         String realm = Config.getDefaultRealm();
282                         // Turn on Console Sysout
283                         System.setOut(System.out);
284                         user = cons.readLine("aaf_id(%s@%s): ", System.getProperty("user.name"), realm);
285                         if (user == null) {
286                             user = System.getProperty("user.name") + '@' + realm;
287                         } else if (user.length() == 0) { //
288                             user = System.getProperty("user.name") + '@' + realm;
289                         } else if ((user.indexOf('@') < 0) && (realm != null)) {
290                             user = user + '@' + realm;
291                         }
292                         access.setProperty(Config.AAF_APPID, user);
293                         diskprops.setProperty(Config.AAF_APPID, user);
294                         encrypted_pass = new String(cons.readPassword("aaf_password: "));
295                         System.setOut(os);
296                         encrypted_pass = Symm.ENC + decryptor.enpass(encrypted_pass);
297                         access.setProperty(Config.AAF_APPPASS, encrypted_pass);
298                         diskprops.setProperty(Config.AAF_APPPASS, encrypted_pass);
299                         diskprops.setProperty(Config.CADI_KEYFILE, access.getProperty(Config.CADI_KEYFILE));
300                     }
301                 }
302             }
303             if (user == null) {
304                 err = new StringBuilder("Add -D" + Config.AAF_APPID + "=<id> ");
305             }
306     
307             if (encrypted_pass == null && alias == null) {
308                 if (err == null) {
309                     err = new StringBuilder();
310                 } else {
311                     err.append("and ");
312                 }
313                 err.append("-D" + Config.AAF_APPPASS + "=<passwd> ");
314             }
315             
316             String cadiLatitude = access.getProperty(Config.CADI_LATITUDE);
317             if (cadiLatitude==null) {
318                 System.out.println("# If you do not know your Global Coordinates, we suggest bing.com/maps");
319                 cadiLatitude=AAFSSO.cons.readLine("cadi_latitude[0.000]=");
320                 if (cadiLatitude==null || cadiLatitude.isEmpty()) {
321                     cadiLatitude="0.000";
322                 }
323                 access.setProperty(Config.CADI_LATITUDE, cadiLatitude);
324                 addProp(Config.CADI_LATITUDE, cadiLatitude);
325                 
326             }
327             String cadiLongitude = access.getProperty(Config.CADI_LONGITUDE);
328             if (cadiLongitude==null) {
329                 cadiLongitude=AAFSSO.cons.readLine("cadi_longitude[0.000]=");
330                 if (cadiLongitude==null || cadiLongitude.isEmpty()) {
331                     cadiLongitude="0.000";
332                 }
333                 access.setProperty(Config.CADI_LONGITUDE, cadiLongitude);
334                 addProp(Config.CADI_LONGITUDE, cadiLongitude);
335             }
336     
337             String cadi_truststore = access.getProperty(Config.CADI_TRUSTSTORE);
338             if (cadi_truststore==null) {
339                 String name; 
340                 String select;
341                 for (File tsf : dot_aaf.listFiles()) {
342                     name = tsf.getName();
343                     if (name.contains("trust") && 
344                             (name.endsWith(".jks") || name.endsWith(".p12"))) {
345                         select = cons.readLine("Use %s for TrustStore? (y/n):",tsf.getName());
346                         if ("y".equalsIgnoreCase(select)) {
347                             cadi_truststore=tsf.getCanonicalPath();
348                             access.setProperty(Config.CADI_TRUSTSTORE, cadi_truststore);
349                             addProp(Config.CADI_TRUSTSTORE, cadi_truststore);
350                             break;
351                         }
352                     }
353                 }
354             }
355             if (cadi_truststore!=null) {
356                 if (cadi_truststore.indexOf(File.separatorChar)<0) {
357                     cadi_truststore=dot_aaf.getPath()+File.separator+cadi_truststore;
358                 }
359                 String cadi_truststore_password = access.getProperty(Config.CADI_TRUSTSTORE_PASSWORD);
360                 if (cadi_truststore_password==null) {
361                     cadi_truststore_password=AAFSSO.cons.readLine("cadi_truststore_password[%s]=","changeit");
362                     cadi_truststore_password = access.encrypt(cadi_truststore_password);
363                     access.setProperty(Config.CADI_TRUSTSTORE_PASSWORD, cadi_truststore_password);
364                     addProp(Config.CADI_TRUSTSTORE_PASSWORD, cadi_truststore_password);
365                 }
366             }
367             ok = err==null;
368         }
369         String locateUrl = Config.getAAFLocateUrl(access);
370         if (locateUrl==null) {
371             locateUrl=AAFSSO.cons.readLine("AAF Locator URL=https://");
372             if (locateUrl==null || locateUrl.length()==0) {
373                 err = new StringBuilder(Config.AAF_LOCATE_URL);
374                 err.append(" is required.");
375                 ok = false;
376                 return;
377             } else {
378                 locateUrl="https://"+locateUrl;
379             }
380             access.setProperty(Config.AAF_LOCATE_URL, locateUrl);
381             addProp(Config.AAF_LOCATE_URL, locateUrl);
382             try {
383                 if(access.getProperty(Config.AAF_URL)==null) {
384                     access.setProperty(Config.AAF_URL, Defaults.AAF_ROOT+".service:"+Defaults.AAF_VERSION);
385                 }
386                 AAFCon<?> aafCon = AAFCon.newInstance(access);
387                 Future<Configuration> acf;
388                 RosettaDF<Configuration> configDF = new RosettaEnv().newDataFactory(Configuration.class);
389                 acf = aafCon.client(new SingleEndpointLocator(locateUrl))
390                         .read("/configure/"+user+"/aaf", configDF);
391                 if (acf.get(aafCon.connTimeout)) {
392                     for(Props p : acf.value.getProps()) {
393                         addProp(p.getTag(),p.getValue());
394                         if(access.getProperty(p.getTag())==null) {
395                             access.setProperty(p.getTag(), p.getValue());
396                         }
397                     }
398                 } else {
399                     access.log(Level.INFO,acf.body());
400                 }
401             } catch (LocatorException | APIException | URISyntaxException e) {
402                 access.log(e);
403             }
404         }
405         
406         final String apiVersion = access.getProperty(Config.AAF_API_VERSION, Config.AAF_DEFAULT_API_VERSION);
407         final String aaf_root_ns = access.getProperty(Config.AAF_ROOT_NS);
408         String locateRoot;
409         if(aaf_root_ns==null) {
410             locateRoot=Defaults.AAF_ROOT;
411         } else {
412             locateRoot = Defaults.AAF_LOCATE_CONST + "/%CNS." + aaf_root_ns;
413         }
414         if(access.getProperty(Config.AAF_URL)==null) {
415             access.setProperty(Config.AAF_URL, locateRoot+".service:"+apiVersion);
416         }
417
418         writeFiles();
419     }
420
421     public void setLogDefault() {
422         this.setLogDefault(PropAccess.DEFAULT);
423         System.setOut(stdOutOrig);
424     }
425
426     public void setStdErrDefault() {
427         access.setLogLevel(PropAccess.DEFAULT);
428         System.setErr(stdErrOrig);
429     }
430
431     public void setLogDefault(Level level) {
432         if (access!=null) {
433             access.setLogLevel(level);
434         }
435         System.setOut(stdOutOrig);
436     }
437
438     public boolean loginOnly() {
439         return loginOnly;
440     }
441
442     public void addProp(String key, String value) {
443         if (key==null || value==null) {
444             return;
445         }
446         touchDiskprops=true;
447         diskprops.setProperty(key, value);
448     }
449
450     public void writeFiles() throws IOException {
451         if (touchDiskprops) {
452             // Store Creds, if they work
453             if (diskprops != null) {
454                 if (!dot_aaf.exists()) {
455                     dot_aaf.mkdirs();
456                 }
457                 FileOutputStream fos = new FileOutputStream(sso);
458                 try {
459                     diskprops.store(fos, "AAF Single Signon");
460                 } finally {
461                     fos.close();
462                 }
463             }
464             if (sso != null) {
465                 setReadonly(sso);
466                 sso.setWritable(true, true);
467             }
468         }
469     }
470
471     public PropAccess access() {
472         return access;
473     }
474
475     public StringBuilder err() {
476         return err;
477     }
478
479     public String user() {
480         return user;
481     }
482
483     public String enc_pass() {
484         return encrypted_pass;
485     }
486
487     public boolean useX509() {
488         return use_X509;
489     }
490
491     public void close() {
492         if (close != null) {
493             try {
494                 close.invoke(null);
495             } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
496                 // nothing to do here.
497             }
498             close = null;
499         }
500     }
501
502     private List<String> parseArgs(String[] args)
503     {
504         List<String> larg = new ArrayList<>(args.length);
505
506         // Cover for bash's need to escape *.. (\\*)
507         // also, remove SSO if required
508         for (int i = 0; i < args.length; ++i) {
509             if ("\\*".equals(args[i])) {
510                 args[i] = "*";
511             }
512
513             if ("-logout".equalsIgnoreCase(args[i])) {
514                 removeSSO = true;
515             } else if ("-login".equalsIgnoreCase(args[i])) {
516                 loginOnly = true;
517             } else if ("-noexit".equalsIgnoreCase(args[i])) {
518                 doExit = false;
519             } else {
520                 larg.add(args[i]);
521             }
522         }
523         return larg;
524     }
525     
526     private void setReadonly(File file) {
527         file.setExecutable(false, false);
528         file.setWritable(false, false);
529         file.setReadable(false, false);
530         file.setReadable(true, true);
531     }
532
533     public boolean ok() {
534         return ok;
535     }
536     
537     public static interface ProcessArgs {
538         public Properties process(final String[] args, final Properties props);
539     }
540 }