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