2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.cadi.sso;
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;
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;
55 import locate.v1_1.Configuration;
56 import locate.v1_1.Configuration.Props;
59 public static final MyConsole cons = TheConsole.implemented() ? new TheConsole() : new SubStandardConsole();
60 // private static final int EIGHT_HOURS = 8 * 60 * 60 * 1000;
62 private Properties diskprops;
63 private boolean touchDiskprops;
64 private File dot_aaf = null;
65 private File sso = null; // instantiated, if ever, with diskprops
67 boolean removeSSO = false;
68 boolean loginOnly = false;
69 boolean doExit = true;
70 private PropAccess access;
71 private StringBuilder err;
73 private String encrypted_pass;
74 private boolean use_X509;
76 private PrintStream os;
79 private final PrintStream stdOutOrig;
80 private final PrintStream stdErrOrig;
83 public AAFSSO(String[] args) throws IOException, CadiException {
84 this(args,new Properties());
87 public AAFSSO(String[] args, ProcessArgs pa) throws IOException, CadiException {
88 this(args,pa.process(args, new Properties()));
91 public AAFSSO(String[] args, Properties dp) throws IOException, CadiException {
92 stdOutOrig = System.out;
93 stdErrOrig = System.err;
95 List<String> nargs = parseArgs(args);
97 touchDiskprops = false;
99 dot_aaf = new File(System.getProperty("user.home") + "/.aaf");
100 if (!dot_aaf.exists()) {
103 File f = new File(dot_aaf, "sso.out");
104 os = new PrintStream(new FileOutputStream(f, true));
108 sso = new File(dot_aaf, "sso.props");
110 InputStream propStream = new FileInputStream(sso);
112 diskprops.load(propStream);
118 File dot_aaf_kf = new File(dot_aaf, "keyfile");
121 if (dot_aaf_kf.exists()) {
122 dot_aaf_kf.setWritable(true, true);
126 Properties temp = new Properties();
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());
136 touchDiskprops = true;
138 String[] naargs = new String[nargs.size()];
139 nargs.toArray(naargs);
140 access = new PropAccess(os, naargs);
143 System.out.println("AAF SSO information removed");
145 // Config.setDefaultRealm(access);
147 if (!dot_aaf_kf.exists()) {
148 // This will create, as required, or reuse
149 ArtifactDir.getSymm(dot_aaf_kf);
152 for (Entry<Object, Object> es : diskprops.entrySet()) {
153 nargs.add(es.getKey().toString() + '=' + es.getValue().toString());
155 String[] naargs = new String[nargs.size()];
156 nargs.toArray(naargs);
157 access = new PropAccess(os, naargs);
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);
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);
172 // touchDiskprops=true;
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());
183 alias = access.getProperty(Config.CADI_ALIAS);
185 appID = access.getProperty(Config.AAF_APPID);
192 String aaf_container_ns = "";
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);
203 aaf_container_ns = cons.readLine("Container Namespace (blank if none)? [\"\"]: ", aaf_container_ns);
205 diskprops.setProperty(Config.AAF_APPID,appID);
208 String keystore=access.getProperty(Config.CADI_KEYSTORE);
209 String keystore_pass=access.getProperty(Config.CADI_KEYSTORE_PASSWORD);
211 if (user==null || (alias!=null && (keystore==null || keystore_pass==null))) {
212 String select = null;
214 for (File tsf : dot_aaf.listFiles()) {
215 name = tsf.getName();
216 if (!name.contains("trust") && (name.endsWith(".jks") || name.endsWith(".p12"))) {
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);
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);
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);
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);
251 keystore_pass = encrypted_pass;
260 Symm decryptor = ArtifactDir.getSymm(dot_aaf_kf);
263 String cm_url = access.getProperty(Config.AAF_URL_CM); // SSO might overwrite...
264 FileInputStream fos = new FileInputStream(sso);
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);
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);
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;
291 access.setProperty(Config.AAF_APPID, user);
292 diskprops.setProperty(Config.AAF_APPID, user);
293 encrypted_pass = new String(cons.readPassword("aaf_password: "));
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));
303 err = new StringBuilder("Add -D" + Config.AAF_APPID + "=<id> ");
306 if (encrypted_pass == null && alias == null) {
308 err = new StringBuilder();
312 err.append("-D" + Config.AAF_APPPASS + "=<passwd> ");
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";
322 access.setProperty(Config.CADI_LATITUDE, cadiLatitude);
323 addProp(Config.CADI_LATITUDE, cadiLatitude);
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";
332 access.setProperty(Config.CADI_LONGITUDE, cadiLongitude);
333 addProp(Config.CADI_LONGITUDE, cadiLongitude);
336 String cadi_truststore = access.getProperty(Config.CADI_TRUSTSTORE);
337 if (cadi_truststore==null) {
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);
354 if (cadi_truststore!=null) {
355 if (cadi_truststore.indexOf(File.separatorChar)<0) {
356 cadi_truststore=dot_aaf.getPath()+File.separator+cadi_truststore;
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);
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.");
377 locateUrl="https://"+locateUrl;
379 access.setProperty(Config.AAF_LOCATE_URL, locateUrl);
380 addProp(Config.AAF_LOCATE_URL, locateUrl);
382 if(access.getProperty(Config.AAF_URL)==null) {
383 access.setProperty(Config.AAF_URL, Defaults.AAF_ROOT+".service:"+Defaults.AAF_VERSION);
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());
398 access.log(Level.INFO,acf.body());
400 } catch (LocatorException | APIException e) {
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);
408 if(aaf_root_ns==null) {
409 locateRoot=Defaults.AAF_ROOT;
411 locateRoot = Defaults.AAF_LOCATE_CONST + "/%CNS." + aaf_root_ns;
413 if(access.getProperty(Config.AAF_URL)==null) {
414 access.setProperty(Config.AAF_URL, locateRoot+".service:"+apiVersion);
420 public void setLogDefault() {
421 this.setLogDefault(PropAccess.DEFAULT);
422 System.setOut(stdOutOrig);
425 public void setStdErrDefault() {
426 access.setLogLevel(PropAccess.DEFAULT);
427 System.setErr(stdErrOrig);
430 public void setLogDefault(Level level) {
432 access.setLogLevel(level);
434 System.setOut(stdOutOrig);
437 public boolean loginOnly() {
441 public void addProp(String key, String value) {
442 if (key==null || value==null) {
446 diskprops.setProperty(key, value);
449 public void writeFiles() throws IOException {
450 if (touchDiskprops) {
451 // Store Creds, if they work
452 if (diskprops != null) {
453 if (!dot_aaf.exists()) {
456 FileOutputStream fos = new FileOutputStream(sso);
458 diskprops.store(fos, "AAF Single Signon");
465 sso.setWritable(true, true);
470 public PropAccess access() {
474 public StringBuilder err() {
478 public String user() {
482 public String enc_pass() {
483 return encrypted_pass;
486 public boolean useX509() {
490 public void close() {
494 } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
495 // nothing to do here.
501 private List<String> parseArgs(String[] args)
503 List<String> larg = new ArrayList<>(args.length);
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])) {
512 if ("-logout".equalsIgnoreCase(args[i])) {
514 } else if ("-login".equalsIgnoreCase(args[i])) {
516 } else if ("-noexit".equalsIgnoreCase(args[i])) {
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);
532 public boolean ok() {
536 public static interface ProcessArgs {
537 public Properties process(final String[] args, final Properties props);