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