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