Minor changes for Organization needs
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / PropAccess.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  *
7  * Modifications Copyright (C) 2018 IBM.
8  * ===========================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END====================================================
21  *
22  */
23
24 package org.onap.aaf.cadi;
25
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.FileInputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.PrintStream;
32 import java.io.PrintWriter;
33 import java.io.StringWriter;
34 import java.text.DateFormat;
35 import java.text.SimpleDateFormat;
36 import java.util.ArrayList;
37 import java.util.Date;
38 import java.util.List;
39 import java.util.Map.Entry;
40 import java.util.Properties;
41
42 import org.onap.aaf.cadi.config.Config;
43 import org.onap.aaf.cadi.config.SecurityInfo;
44 import org.onap.aaf.cadi.util.Split;
45
46 public class PropAccess implements Access {
47     // Sonar says cannot be static... it's ok.  not too many PropAccesses created.
48     private final SimpleDateFormat iso8601 = newISO8601();
49     private Symm symm;
50     public static final Level DEFAULT = Level.AUDIT;
51     private int level;
52     private Properties props;
53     private List<String> recursionProtection = null;
54     private LogIt logIt;
55     private String name;
56
57     public PropAccess() {
58         logIt = new StreamLogIt(System.out);
59         init(null);
60     }
61
62     /**
63      * This Constructor soly exists to instantiate Servlet Context Based Logging that will call "init" later.
64      * @param sc
65      */
66     protected PropAccess(Object o) {
67         logIt = new StreamLogIt(System.out);
68         props = new Properties();
69     }
70
71     public PropAccess(String ... args) {
72         this(System.out,args);
73     }
74
75     public PropAccess(PrintStream ps, String[] args) {
76         logIt = new StreamLogIt(ps==null?System.out:ps);
77         init(logIt,args);
78     }
79
80     public PropAccess(LogIt logit, String[] args) {
81         init(logit, args);
82     }
83
84     public PropAccess(Properties p) {
85         this(System.out,p);
86     }
87
88     public PropAccess(PrintStream ps, Properties p) {
89         logIt = new StreamLogIt(ps==null?System.out:ps);
90         init(p);
91     }
92
93     protected void init(final LogIt logIt, final String[] args) {
94         this.logIt = logIt;
95         Properties nprops=new Properties();
96         int eq;
97         for (String arg : args) {
98             if ((eq=arg.indexOf('='))>0) {
99                 nprops.setProperty(arg.substring(0, eq),arg.substring(eq+1));
100             }
101         }
102         init(nprops);
103     }
104
105     public static SimpleDateFormat newISO8601() {
106         return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
107     }
108
109     protected synchronized void init(Properties p) {
110         // Make sure these two are set before any changes in Logging
111         name = "cadi";
112
113         props = new Properties();
114         // First, load related System Properties
115         for (Entry<Object,Object> es : System.getProperties().entrySet()) {
116             String key = es.getKey().toString();
117             for (String start : new String[] {"cadi_","aaf_","cm_"}) {
118                 if (key.startsWith(start)) {
119                     props.put(key, es.getValue());
120                 }
121             }
122         }
123         // Second, overlay or fill in with Passed in Props
124         if (p!=null) {
125             props.putAll(p);
126         }
127
128         // Preset LogLevel
129         String sLevel = props.getProperty(Config.CADI_LOGLEVEL);
130         // Third, load any Chained Property Files
131         load(props.getProperty(Config.CADI_PROP_FILES));
132
133         if(sLevel==null) { // if LogLev wasn't set before, check again after Chained Load
134             sLevel = props.getProperty(Config.CADI_LOGLEVEL);
135             if (sLevel==null) {
136                 level=DEFAULT.maskOf();
137             } else {
138                 level=Level.valueOf(sLevel).maskOf();
139             }
140         }
141         // Setup local Symmetrical key encryption
142         if (symm==null) {
143             try {
144                 symm = Symm.obtain(this);
145             } catch (CadiException e) {
146                 System.err.append("FATAL ERROR: Cannot obtain Key Information.");
147                 e.printStackTrace(System.err);
148                 System.exit(1);
149             }
150         }
151
152         name = props.getProperty(Config.CADI_LOGNAME, name);
153
154         SecurityInfo.setHTTPProtocols(this);
155
156     }
157
158
159     private void load(String cadi_prop_files) {
160         if (cadi_prop_files==null) {
161             return;
162         }
163         String prevKeyFile = props.getProperty(Config.CADI_KEYFILE);
164
165
166         for(String filename : Split.splitTrim(File.pathSeparatorChar, cadi_prop_files)) {
167             Properties fileProps = new Properties();
168             File file = new File(filename);
169             if (file.exists()) {
170                 printf(Level.INIT,"Loading CADI Properties from %s",file.getAbsolutePath());
171                 try {
172                     FileInputStream fis = new FileInputStream(file);
173                     try {
174                         fileProps.load(fis);
175                         // Only load props from recursion which are not already in props
176                         // meaning top Property file takes precedence
177                         for(Entry<Object, Object> es : fileProps.entrySet()) {
178                             if(props.get(es.getKey())==null) {
179                                 String key = es.getKey().toString();
180                                 String value = es.getValue().toString();
181                                 props.put(key, value);
182                                 if(key.contains("pass")) {
183                                     value = "vi XX";
184                                 }
185                                 printf(Level.DEBUG,"  %s=%s",key,value);
186                             }
187                         }
188                         // Recursively Load
189                         String chainProp = fileProps.getProperty(Config.CADI_PROP_FILES);
190                         if (chainProp!=null) {
191                             if (recursionProtection==null) {
192                                 recursionProtection = new ArrayList<>();
193                                 recursionProtection.add(cadi_prop_files);
194                             }
195                             if (!recursionProtection.contains(chainProp)) {
196                                 recursionProtection.add(chainProp);
197                                 load(chainProp); // recurse
198                             }
199                         }
200                     } finally {
201                         fis.close();
202                     }
203                 } catch (Exception e) {
204                     log(e,filename,"cannot be opened");
205                 }
206             } else {
207                 printf(Level.WARN,"Warning: recursive CADI Property %s does not exist",file.getAbsolutePath());
208             }
209         }
210
211         // Trim
212         for (Entry<Object, Object> es : props.entrySet()) {
213             Object value = es.getValue();
214             if (value instanceof String) {
215                 String trim = ((String)value).trim();
216                 // Remove Beginning/End Quotes, which might be there if mixed with Bash Props
217                 int s = 0, e=trim.length()-1;
218                 if (s<e && trim.charAt(s)=='"' && trim.charAt(e)=='"') {
219                     trim=trim.substring(s+1,e);
220                 }
221                 if (trim!=value) { // Yes, I want OBJECT equals
222                     props.setProperty((String)es.getKey(), trim);
223                 }
224             }
225         }
226         // Reset Symm if Keyfile Changes:
227         String newKeyFile = props.getProperty(Config.CADI_KEYFILE);
228         if ((prevKeyFile!=null && newKeyFile!=null) || (newKeyFile!=null && !newKeyFile.equals(prevKeyFile))) {
229             try {
230                 symm = Symm.obtain(this);
231             } catch (CadiException e) {
232                 System.err.append("FATAL ERROR: Cannot obtain Key Information.");
233                 e.printStackTrace(System.err);
234                 System.exit(1);
235             }
236
237             prevKeyFile=newKeyFile;
238         }
239
240         String loglevel = props.getProperty(Config.CADI_LOGLEVEL);
241         if (loglevel!=null) {
242             try {
243                 level=Level.valueOf(loglevel).maskOf();
244             } catch (IllegalArgumentException e) {
245                 printf(Level.ERROR,"%s=%s is an Invalid Log Level",Config.CADI_LOGLEVEL,loglevel);
246             }
247         }
248     }
249
250     @Override
251     public void load(InputStream is) throws IOException {
252         props.load(is);
253         load(props.getProperty(Config.CADI_PROP_FILES));
254     }
255
256     @Override
257     public void log(Level level, Object ... elements) {
258         if (willLog(level)) {
259             logIt.push(level,elements);
260         }
261     }
262
263     public StringBuilder buildMsg(Level level, Object[] elements) {
264         return buildMsg(name,iso8601,level,elements);
265     }
266
267     /*
268      * Need to pass in DateFormat per thread, because not marked as thread safe
269      */
270     public static StringBuilder buildMsg(final String name, final DateFormat sdf, Level level, Object[] elements) {
271         final StringBuilder sb;
272         int end = elements.length;
273         if(sdf==null) {
274             sb = new StringBuilder();
275             write(true,sb,elements);
276         } else {
277             sb = new StringBuilder(
278                     sdf.format(new Date())
279                     );
280             sb.append(' ');
281             sb.append(level.name());
282             sb.append(" [");
283             sb.append(name);
284             if (end<=0) {
285                 sb.append("] ");
286             } else {
287                 int idx = 0;
288                 if(elements[idx]!=null  &&
289                     elements[idx] instanceof Integer) {
290                     sb.append('-');
291                     sb.append(elements[idx]);
292                     ++idx;
293                 }
294                 sb.append("] ");
295                 write(true,sb,elements);
296             }
297         }
298         return sb;
299     }
300
301     private static boolean write(boolean first, StringBuilder sb, Object[] elements) {
302         String s;
303         for (Object o : elements) {
304             if (o!=null) {
305                 if(o.getClass().isArray()) {
306                     first = write(first,sb,(Object[])o);
307                 } else if(o instanceof Throwable) {
308                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
309                     PrintStream ps = new PrintStream(baos);
310                     ((Throwable)o).printStackTrace(ps);
311                     sb.append(baos.toString());
312                 } else {
313                     s=o.toString();
314                     if (first) {
315                         first = false;
316                     } else {
317                         int l = s.length();
318                         if (l>0)    {
319                             switch(s.charAt(l-1)) {
320                                 case ' ':
321                                     break;
322                                 default:
323                                     sb.append(' ');
324                             }
325                         }
326                     }
327                     sb.append(s);
328                 }
329             }
330         }
331         return first;
332     }
333
334     @Override
335     public void log(Exception e, Object... elements) {
336         StringWriter sw = new StringWriter();
337         PrintWriter pw = new PrintWriter(sw);
338         pw.println();
339         e.printStackTrace(pw);
340         log(Level.ERROR,elements,sw.toString());
341     }
342
343     @Override
344     public void printf(Level level, String fmt, Object... elements) {
345         if (willLog(level)) {
346             log(level,String.format(fmt, elements));
347         }
348     }
349
350     @Override
351     public void setLogLevel(Level level) {
352         this.level = level.maskOf();
353     }
354
355     @Override
356     public boolean willLog(Level level) {
357         return level.inMask(this.level);
358     }
359
360     @Override
361     public ClassLoader classLoader() {
362         return ClassLoader.getSystemClassLoader();
363     }
364
365     @Override
366     public String getProperty(String tag, String def) {
367         return props.getProperty(tag,def);
368     }
369
370     @Override
371     public String decrypt(String encrypted, boolean anytext) throws IOException {
372         return (encrypted!=null && (anytext==true || encrypted.startsWith(Symm.ENC)))
373             ? symm.depass(encrypted)
374             : encrypted;
375     }
376
377     public String encrypt(String unencrypted) throws IOException {
378         return Symm.ENC+symm.enpass(unencrypted);
379     }
380
381     //////////////////
382     // Additional
383     //////////////////
384     public String getProperty(String tag) {
385         return props.getProperty(tag);
386     }
387
388
389     public Properties getProperties() {
390         return props;
391     }
392
393     public void setProperty(String tag, String value) {
394         if (value!=null) {
395             props.put(tag, value);
396             if (Config.CADI_KEYFILE.equals(tag)) {
397                 // reset decryption too
398                 try {
399                     symm = Symm.obtain(this);
400                 } catch (CadiException e) {
401                     System.err.append("FATAL ERROR: Cannot obtain Key Information.");
402                     e.printStackTrace(System.err);
403                     System.exit(1);
404                 }
405             }
406         }
407     }
408
409     public interface LogIt {
410         public void push(Level level, Object ... elements) ;
411     }
412
413     private class StreamLogIt implements LogIt {
414         private PrintStream ps;
415
416         public StreamLogIt(PrintStream ps) {
417             this.ps = ps;
418         }
419         @Override
420         public void push(Level level, Object ... elements) {
421             ps.println(buildMsg(level,elements));
422             ps.flush();
423         }
424     }
425
426     public void set(LogIt logit) {
427         logIt = logit;
428     }
429
430     public void setStreamLogIt(PrintStream ps) {
431         logIt = new StreamLogIt(ps);
432     }
433
434     public String toString() {
435         return props.toString();
436     }
437 }