Mass removal of all Tabs (Style Warnings)
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / filter / CadiFilter.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.filter;
23
24 import java.io.IOException;
25 import java.lang.reflect.Constructor;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import javax.servlet.Filter;
30 import javax.servlet.FilterChain;
31 import javax.servlet.FilterConfig;
32 import javax.servlet.ServletException;
33 import javax.servlet.ServletRequest;
34 import javax.servlet.ServletResponse;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.onap.aaf.cadi.Access;
39 import org.onap.aaf.cadi.Access.Level;
40 import org.onap.aaf.cadi.CadiException;
41 import org.onap.aaf.cadi.CadiWrap;
42 import org.onap.aaf.cadi.LocatorException;
43 import org.onap.aaf.cadi.Lur;
44 import org.onap.aaf.cadi.PropAccess;
45 import org.onap.aaf.cadi.ServletContextAccess;
46 import org.onap.aaf.cadi.TrustChecker;
47 import org.onap.aaf.cadi.config.Config;
48 import org.onap.aaf.cadi.config.Get;
49 import org.onap.aaf.cadi.taf.TafResp;
50 import org.onap.aaf.cadi.taf.TafResp.RESP;
51 import org.onap.aaf.cadi.util.Timing;
52
53 /**
54  * CadiFilter
55  * 
56  * This class implements Servlet Filter, and ties together CADI implementations
57  * 
58  * This class can be used in a standard J2EE Servlet manner.  Optimal usage is for POJO operations, where
59  * one can enforce this Filter being first and primary.  Depending on the Container, it 
60  * may be more effective, in some cases, to utilize features that allow earlier determination of 
61  * AUTHN (Authorization).  An example would be "Tomcat Valve".  These implementations, however, should
62  * be modeled after the "init" and "doFilter" functions, and be kept up to date as this class changes.
63  * 
64  * 
65  * @author Jonathan
66  *
67  */
68 public class CadiFilter implements Filter {
69     private static CadiHTTPManip httpChecker;
70     private static String[] pathExceptions;
71     private static List<Pair> mapPairs;
72     private Access access;
73     private Object[] additionalTafLurs;
74     private Filter oauthFilter;
75     private static int count=0;
76     
77     public Lur getLur() {
78         return httpChecker.getLur();
79     }
80     
81     /**
82      * Construct a viable Filter
83      * 
84      * Due to the vagaries of many containers, there is a tendency to create Objects and call "Init" on 
85      * them at a later time.  Therefore, this object creates with an object that denies all access
86      * until appropriate Init happens, just in case the container lets something slip by in the meantime.
87      * 
88      */
89     public CadiFilter() {
90         additionalTafLurs = CadiHTTPManip.noAdditional;
91     }
92
93     /**
94      * This constructor to be used when directly constructing and placing in HTTP Engine
95      * 
96      * @param access
97      * @param moreTafLurs
98      * @throws ServletException 
99      */
100     public CadiFilter(Access access, Object ... moreTafLurs) throws ServletException {
101         additionalTafLurs = moreTafLurs;
102         init(new AccessGetter(this.access = access));
103     }
104
105
106     /**
107      * Use this to pass in a PreContructed CADI Filter, but with initializing... let Servlet do it
108      * @param init
109      * @param access
110      * @param moreTafLurs
111      * @throws ServletException
112      */
113     public CadiFilter(boolean init, PropAccess access, Object ... moreTafLurs) throws ServletException {
114         this.access = access;
115         additionalTafLurs = moreTafLurs;
116         if(init) {
117             init(new AccessGetter(access));
118         }
119     }
120
121     /**
122      * Init
123      * 
124      * Standard Filter "init" call with FilterConfig to obtain properties.  POJOs can construct a
125      * FilterConfig with the mechanism of their choice, and standard J2EE Servlet engines utilize this
126      * mechanism already.
127      */
128     //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM Init functions
129     public void init(FilterConfig filterConfig) throws ServletException {
130         // need the Context for Logging, instantiating ClassLoader, etc
131         ServletContextAccess sca=new ServletContextAccess(filterConfig); 
132         if(access==null) {
133             access = sca;
134         }
135         
136         // Set Protected getter with base Access, for internal class instantiations
137         init(new FCGet(access, sca.context(), filterConfig));
138     }
139     
140
141     @SuppressWarnings("unchecked")
142     private void init(Get getter) throws ServletException {
143         // Start with the assumption of "Don't trust anyone".
144        TrustChecker tc = TrustChecker.NOTRUST; // default position
145        try {
146            Class<TrustChecker> ctc = (Class<TrustChecker>) Class.forName("org.onap.aaf.cadi.aaf.v2_0.AAFTrustChecker");
147            if(ctc!=null) {
148                Constructor<TrustChecker> contc = ctc.getConstructor(Access.class);
149                if(contc!=null) {
150                    tc = contc.newInstance(access);
151                }
152            }
153        } catch (Exception e) {
154            access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
155        }
156        
157        try {
158            Class<Filter> cf=null;
159            try {
160                cf= (Class<Filter>) Class.forName("org.onap.aaf.cadi.oauth.OAuthFilter");
161                oauthFilter = cf.newInstance();
162            } catch (ClassNotFoundException e) {
163                oauthFilter = new Filter() { // Null Filter
164                     @Override
165                     public void destroy() {
166                     }
167     
168                     @Override
169                     public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)throws IOException, ServletException {
170                         chain.doFilter(req, resp);
171                     }
172     
173                     @Override
174                     public void init(FilterConfig arg0) throws ServletException {
175                     }
176                };
177            }
178        } catch (Exception e) {
179            access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
180        }
181
182         
183         // Synchronize, because some instantiations call init several times on the same object
184         // In this case, the epiTaf will be changed to a non-NullTaf, and thus not instantiate twice.
185         synchronized(CadiHTTPManip.noAdditional /*will always remain same Object*/) {
186             ++count;
187             if(httpChecker == null) {
188                 if(access==null) {
189                     access = new PropAccess();
190                 }
191                 try {
192                     httpChecker = new CadiHTTPManip(access,null /*reuseable Con*/,tc, additionalTafLurs);
193                 } catch (CadiException | LocatorException e1) {
194                     throw new ServletException(e1);
195                 }
196             } else if(access==null) {
197                 access= httpChecker.getAccess();
198             }
199
200             /*
201              * Setup Authn Path Exceptions
202              */
203             if(pathExceptions==null) {
204                 String str = getter.get(Config.CADI_NOAUTHN, null, true);
205                 if(str!=null) {
206                     pathExceptions = str.split("\\s*:\\s*");
207                 }
208             }
209     
210             /* 
211              * SETUP Permission Converters... those that can take Strings from a Vendor Product, and convert to appropriate AAF Permissions
212              */
213             if(mapPairs==null) {
214                 String str = getter.get(Config.AAF_PERM_MAP, null, true);
215                 if(str!=null) {
216                     String mstr = getter.get(Config.AAF_PERM_MAP, null, true);
217                     if(mstr!=null) {
218                         String map[] = mstr.split("\\s*:\\s*");
219                         if(map.length>0) {
220                             MapPermConverter mpc=null;
221                             int idx;
222                             mapPairs = new ArrayList<>();
223                             for(String entry : map) {
224                                 if((idx=entry.indexOf('='))<0) { // it's a Path, so create a new converter
225                                     access.log(Level.INIT,"Loading Perm Conversions for:",entry);
226                                     mapPairs.add(new Pair(entry,mpc=new MapPermConverter()));
227                                 } else {
228                                     if(mpc!=null) {
229                                         mpc.map().put(entry.substring(0,idx),entry.substring(idx+1));
230                                     } else {
231                                         access.log(Level.ERROR,"cadi_perm_map is malformed; ",entry, "is skipped");
232                                     }
233                                 }
234                             }
235                         }
236                     }
237                 }
238             }
239         }
240
241         // Remove Getter
242         getter = Get.NULL;
243     }
244
245     /**
246      * Containers call "destroy" when time to cleanup 
247      */
248     public void destroy() {
249         // Synchronize, in case multiCadiFilters are used.
250         synchronized(CadiHTTPManip.noAdditional) {
251             if(--count<=0 && httpChecker!=null) {
252                 httpChecker.destroy();
253                 httpChecker=null;
254                 access=null;
255                 pathExceptions=null;
256             }
257         }
258     }
259
260     /**
261      * doFilter
262      * 
263      * This is the standard J2EE invocation.  Analyze the request, modify response as necessary, and
264      * only call the next item in the filterChain if request is suitably Authenticated.
265      */
266     //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions
267     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
268         final long startAll = System.nanoTime();
269         long startCode, startValidate;
270         float code=0f, validate=0f;
271         String user = "n/a";
272         String tag = "";
273         try {
274             HttpServletRequest hreq = (HttpServletRequest)request;
275             if(noAuthn(hreq)) {
276                 startCode=System.nanoTime();
277                 chain.doFilter(request, response);
278                 code = Timing.millis(startCode);
279             } else {
280                 HttpServletResponse hresp = (HttpServletResponse)response;
281                 startValidate=System.nanoTime();
282                 TafResp tresp = httpChecker.validate(hreq, hresp, hreq);
283                 validate = Timing.millis(startValidate);
284                 if(tresp.isAuthenticated()==RESP.IS_AUTHENTICATED) {
285                     user = tresp.getPrincipal().personalName();
286                     tag = tresp.getPrincipal().tag();
287                     CadiWrap cw = new CadiWrap(hreq, tresp, httpChecker.getLur(),getConverter(hreq));
288                     if(httpChecker.notCadi(cw, hresp)) {
289                         startCode=System.nanoTime();
290                         oauthFilter.doFilter(cw,response,chain);
291                         code = Timing.millis(startCode);
292                     }
293                 }
294             }
295         } catch (ClassCastException e) {
296             throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e);
297         } finally {
298             access.printf(Level.WARN, "Trans: user=%s[%s],ip=%s,ms=%f,validate=%f,code=%f",
299                 user,tag,request.getRemoteAddr(),
300                 Timing.millis(startAll),validate,code);
301         }
302     }
303
304
305     /** 
306      * If PathExceptions exist, report if these should not have Authn applied.
307      * @param hreq
308      * @return
309      */
310     private boolean noAuthn(HttpServletRequest hreq) {
311         if(pathExceptions!=null) {
312             String pi = hreq.getPathInfo();
313             if(pi==null) return false; // JBoss sometimes leaves null
314             for(String pe : pathExceptions) {
315                 if(pi.startsWith(pe))return true;
316             }
317         }
318         return false;
319     }
320     
321     /**
322      * Get Converter by Path
323      */
324     private PermConverter getConverter(HttpServletRequest hreq) {
325         if(mapPairs!=null) {
326             String pi = hreq.getPathInfo();
327             if(pi !=null) {
328                 for(Pair p: mapPairs) {
329                     if(pi.startsWith(p.name))return p.pc;
330                 }
331             }
332         }
333         return NullPermConverter.singleton();
334     }
335     
336     /**
337      * store PermConverters by Path prefix
338      * @author Jonathan
339      *
340      */
341     private class Pair {
342         public Pair(String key, PermConverter pc) {
343             name = key;
344             this.pc = pc;
345         }
346         public String name;
347         public PermConverter pc;
348     }
349
350 }
351