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