0b8bb8fc2b9b6391d14ebe8f9e1c449250ab51cf
[aaf/cadi.git] / core / src / main / java / org / onap / aaf / cadi / filter / CadiFilter.java
1 /*******************************************************************************\r
2  * ============LICENSE_START====================================================\r
3  * * org.onap.aaf\r
4  * * ===========================================================================\r
5  * * Copyright © 2017 AT&T Intellectual Property. All rights reserved.\r
6  * * ===========================================================================\r
7  * * Licensed under the Apache License, Version 2.0 (the "License");\r
8  * * you may not use this file except in compliance with the License.\r
9  * * You may obtain a copy of the License at\r
10  * * \r
11  *  *      http://www.apache.org/licenses/LICENSE-2.0\r
12  * * \r
13  *  * Unless required by applicable law or agreed to in writing, software\r
14  * * distributed under the License is distributed on an "AS IS" BASIS,\r
15  * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
16  * * See the License for the specific language governing permissions and\r
17  * * limitations under the License.\r
18  * * ============LICENSE_END====================================================\r
19  * *\r
20  * * ECOMP is a trademark and service mark of AT&T Intellectual Property.\r
21  * *\r
22  ******************************************************************************/\r
23 package org.onap.aaf.cadi.filter;\r
24 \r
25 import java.io.IOException;\r
26 import java.lang.reflect.Constructor;\r
27 import java.util.ArrayList;\r
28 import java.util.List;\r
29 \r
30 import javax.servlet.Filter;\r
31 import javax.servlet.FilterChain;\r
32 import javax.servlet.FilterConfig;\r
33 import javax.servlet.ServletException;\r
34 import javax.servlet.ServletRequest;\r
35 import javax.servlet.ServletResponse;\r
36 import javax.servlet.http.HttpServletRequest;\r
37 import javax.servlet.http.HttpServletResponse;\r
38 \r
39 import org.onap.aaf.cadi.Access;\r
40 import org.onap.aaf.cadi.CadiException;\r
41 import org.onap.aaf.cadi.CadiWrap;\r
42 import org.onap.aaf.cadi.Lur;\r
43 import org.onap.aaf.cadi.PropAccess;\r
44 import org.onap.aaf.cadi.ServletContextAccess;\r
45 import org.onap.aaf.cadi.TrustChecker;\r
46 import org.onap.aaf.cadi.Access.Level;\r
47 import org.onap.aaf.cadi.config.Config;\r
48 import org.onap.aaf.cadi.config.Get;\r
49 import org.onap.aaf.cadi.taf.TafResp;\r
50 import org.onap.aaf.cadi.taf.TafResp.RESP;\r
51 \r
52 /**\r
53  * CadiFilter\r
54  * \r
55  * This class implements Servlet Filter, and ties together CADI implementations\r
56  * \r
57  * This class can be used in a standard J2EE Servlet manner.  Optimal usage is for POJO operations, where\r
58  * one can enforce this Filter being first and primary.  Depending on the Container, it \r
59  * may be more effective, in some cases, to utilize features that allow earlier determination of \r
60  * AUTHN (Authorization).  An example would be "Tomcat Valve".  These implementations, however, should\r
61  * be modeled after the "init" and "doFilter" functions, and be kept up to date as this class changes.\r
62  * \r
63  * \r
64  *\r
65  */\r
66 public class CadiFilter implements Filter {\r
67         private static CadiHTTPManip httpChecker;\r
68         private static String[] pathExceptions;\r
69         private static List<Pair> mapPairs;\r
70         private Access access;\r
71         private Object[] additionalTafLurs;\r
72         private static int count=0;\r
73         \r
74         public Lur getLur() {\r
75                 return httpChecker.getLur();\r
76         }\r
77         \r
78         /**\r
79          * Construct a viable Filter\r
80          * \r
81          * Due to the vagaries of many containers, there is a tendency to create Objects and call "Init" on \r
82          * them at a later time.  Therefore, this object creates with an object that denies all access\r
83          * until appropriate Init happens, just in case the container lets something slip by in the meantime.\r
84          * \r
85          */\r
86         public CadiFilter() {\r
87                 additionalTafLurs = CadiHTTPManip.noAdditional;\r
88         }\r
89 \r
90         /**\r
91          * This constructor to be used when directly constructing and placing in HTTP Engine\r
92          * \r
93          * @param access\r
94          * @param moreTafLurs\r
95          * @throws ServletException \r
96          */\r
97         public CadiFilter(Access access, Object ... moreTafLurs) throws ServletException {\r
98                 additionalTafLurs = moreTafLurs;\r
99                 init(new AccessGetter(this.access = access));\r
100         }\r
101 \r
102 \r
103         /**\r
104          * Use this to pass in a PreContructed CADI Filter, but with initializing... let Servlet do it\r
105          * @param init\r
106          * @param access\r
107          * @param moreTafLurs\r
108          * @throws ServletException\r
109          */\r
110         public CadiFilter(boolean init, PropAccess access, Object ... moreTafLurs) throws ServletException {\r
111                 this.access = access;\r
112                 if(init) {\r
113                         init(new AccessGetter(access));\r
114                 }\r
115                 additionalTafLurs = moreTafLurs;\r
116         }\r
117 \r
118         /**\r
119          * Init\r
120          * \r
121          * Standard Filter "init" call with FilterConfig to obtain properties.  POJOs can construct a\r
122          * FilterConfig with the mechanism of their choice, and standard J2EE Servlet engines utilize this\r
123          * mechanism already.\r
124          */\r
125         //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM Init functions\r
126         public void init(FilterConfig filterConfig) throws ServletException {\r
127                 // need the Context for Logging, instantiating ClassLoader, etc\r
128                 ServletContextAccess sca=new ServletContextAccess(filterConfig); \r
129                 if(access==null) {\r
130                         access = sca;\r
131                 }\r
132                 \r
133                 // Set Protected getter with base Access, for internal class instantiations\r
134                 init(new FCGet(access, sca.context(), filterConfig));\r
135         }\r
136         \r
137 \r
138    private void init(Get getter) throws ServletException {\r
139         // Start with the assumption of "Don't trust anyone".\r
140            TrustChecker tc = TrustChecker.NOTRUST; // default position\r
141            try {\r
142                    @SuppressWarnings("unchecked")\r
143                    Class<TrustChecker> ctc = (Class<TrustChecker>) Class.forName("com.att.cadi.aaf.v2_0.AAFTrustChecker");\r
144                    if(ctc!=null) {\r
145                            Constructor<TrustChecker> contc = ctc.getConstructor(Access.class);\r
146                            if(contc!=null) {\r
147                                    tc = contc.newInstance(access);\r
148                            }\r
149                    }\r
150            } catch (Exception e) {\r
151                    access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());\r
152            }\r
153        \r
154         \r
155         // Synchronize, because some instantiations call init several times on the same object\r
156         // In this case, the epiTaf will be changed to a non-NullTaf, and thus not instantiate twice.\r
157                 synchronized(CadiHTTPManip.noAdditional /*will always remain same Object*/) {\r
158                         ++count;\r
159                         if(httpChecker == null) {\r
160                                 if(access==null) {\r
161                                         access = new PropAccess();\r
162                                 }\r
163                                 try {\r
164                                         httpChecker = new CadiHTTPManip(access,null /*reuseable Con*/,tc, additionalTafLurs);\r
165                                 } catch (CadiException e1) {\r
166                                         throw new ServletException(e1);\r
167                                 }\r
168                         } else if(access==null) {\r
169                                 access= httpChecker.getAccess();\r
170                         }\r
171 \r
172                         /*\r
173                          * Setup Authn Path Exceptions\r
174                          */\r
175                         if(pathExceptions==null) {\r
176                                 String str = getter.get(Config.CADI_NOAUTHN, null, true);\r
177                                 if(str!=null) {\r
178                                         pathExceptions = str.split("\\s*:\\s*");\r
179                                 }\r
180                         }\r
181         \r
182                         /* \r
183                          * SETUP Permission Converters... those that can take Strings from a Vendor Product, and convert to appropriate AAF Permissions\r
184                          */\r
185                         if(mapPairs==null) {\r
186                                 String str = getter.get(Config.AAF_PERM_MAP, null, true);\r
187                                 if(str!=null) {\r
188                                         String mstr = getter.get(Config.AAF_PERM_MAP, null, true);\r
189                                         if(mstr!=null) {\r
190                                                 String map[] = mstr.split("\\s*:\\s*");\r
191                                                 if(map.length>0) {\r
192                                                         MapPermConverter mpc=null;\r
193                                                         int idx;\r
194                                                         mapPairs = new ArrayList<Pair>();\r
195                                                         for(String entry : map) {\r
196                                                                 if((idx=entry.indexOf('='))<0) { // it's a Path, so create a new converter\r
197                                                                         access.log(Level.INIT,"Loading Perm Conversions for:",entry);\r
198                                                                         mapPairs.add(new Pair(entry,mpc=new MapPermConverter()));\r
199                                                                 } else {\r
200                                                                         if(mpc!=null) {\r
201                                                                                 mpc.map().put(entry.substring(0,idx),entry.substring(idx+1));\r
202                                                                         } else {\r
203                                                                                 access.log(Level.ERROR,"cadi_perm_map is malformed; ",entry, "is skipped");\r
204                                                                         }\r
205                                                                 }\r
206                                                         }\r
207                                                 }\r
208                                         }\r
209                                 }\r
210                         }\r
211                 }\r
212 \r
213                 // Remove Getter\r
214         getter = Get.NULL;\r
215         }\r
216 \r
217         /**\r
218          * Containers call "destroy" when time to cleanup \r
219          */\r
220         public void destroy() {\r
221                 // Synchronize, in case multiCadiFilters are used.\r
222                 synchronized(CadiHTTPManip.noAdditional) {\r
223                         if(--count<=0 && httpChecker!=null) {\r
224                                 httpChecker.destroy();\r
225                                 httpChecker=null;\r
226                                 access=null;\r
227                                 pathExceptions=null;\r
228                         }\r
229                 }\r
230         }\r
231 \r
232         /**\r
233          * doFilter\r
234          * \r
235          * This is the standard J2EE invocation.  Analyze the request, modify response as necessary, and\r
236          * only call the next item in the filterChain if request is suitably Authenticated.\r
237          */\r
238         //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions\r
239         public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {\r
240                 try {\r
241                         HttpServletRequest hreq = (HttpServletRequest)request;\r
242                         if(noAuthn(hreq)) {\r
243                                 chain.doFilter(request, response);\r
244                         } else {\r
245                                 HttpServletResponse hresp = (HttpServletResponse)response;\r
246                                 TafResp tresp = httpChecker.validate(hreq, hresp);\r
247                                 if(tresp.isAuthenticated()==RESP.IS_AUTHENTICATED) {\r
248                                                 CadiWrap cw = new CadiWrap(hreq, tresp, httpChecker.getLur(),getConverter(hreq));\r
249                                                 if(httpChecker.notCadi(cw, hresp)) {\r
250                                                         chain.doFilter(cw,response);\r
251                                                 }\r
252                                 }                                               \r
253                         }\r
254                 } catch (ClassCastException e) {\r
255                         throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e);\r
256                 }\r
257         }\r
258 \r
259 \r
260         /** \r
261          * If PathExceptions exist, report if these should not have Authn applied.\r
262          * @param hreq\r
263          * @return\r
264          */\r
265         private boolean noAuthn(HttpServletRequest hreq) {\r
266                 if(pathExceptions!=null) {\r
267                         String pi = hreq.getPathInfo();\r
268                         if(pi==null) return false; // JBoss sometimes leaves null\r
269                         for(String pe : pathExceptions) {\r
270                                 if(pi.startsWith(pe))return true;\r
271                         }\r
272                 }\r
273                 return false;\r
274         }\r
275         \r
276         /**\r
277          * Get Converter by Path\r
278          */\r
279         private PermConverter getConverter(HttpServletRequest hreq) {\r
280                 if(mapPairs!=null) {\r
281                         String pi = hreq.getPathInfo();\r
282                         if(pi!=null) {\r
283                         for(Pair p: mapPairs) {\r
284                                 if(pi.startsWith(p.name))return p.pc;\r
285                         }\r
286                         }\r
287                 }\r
288                 return NullPermConverter.singleton();\r
289         }\r
290         \r
291         /**\r
292          * store PermConverters by Path prefix\r
293          *\r
294          */\r
295         private class Pair {\r
296                 public Pair(String key, PermConverter pc) {\r
297                         name = key;\r
298                         this.pc = pc;\r
299                 }\r
300                 public String name;\r
301                 public PermConverter pc;\r
302         }\r
303 \r
304 }\r
305 \r