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