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