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