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