2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.cadi.filter;
24 import java.io.IOException;
25 import java.lang.reflect.Constructor;
26 import java.util.ArrayList;
27 import java.util.List;
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;
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;
56 * This class implements Servlet Filter, and ties together CADI implementations
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.
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 SideChain sideChain;
75 private static int count=0;
78 return httpChecker.getLur();
82 * Construct a viable Filter
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.
90 additionalTafLurs = CadiHTTPManip.noAdditional;
94 * This constructor to be used when directly constructing and placing in HTTP Engine
98 * @throws ServletException
100 public CadiFilter(Access access, Object ... moreTafLurs) throws ServletException {
101 additionalTafLurs = moreTafLurs;
102 init(new AccessGetter(this.access = access));
107 * Use this to pass in a PreContructed CADI Filter, but with initializing... let Servlet do it
111 * @throws ServletException
113 public CadiFilter(boolean init, PropAccess access, Object ... moreTafLurs) throws ServletException {
114 this.access = access;
115 additionalTafLurs = moreTafLurs;
117 init(new AccessGetter(access));
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
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);
136 // Set Protected getter with base Access, for internal class instantiations
137 init(new FCGet(access, sca.context(), filterConfig));
141 @SuppressWarnings("unchecked")
142 protected void init(Get getter) throws ServletException {
143 sideChain = new SideChain();
144 // Start with the assumption of "Don't trust anyone".
145 TrustChecker tc = TrustChecker.NOTRUST; // default position
147 Class<TrustChecker> ctc = (Class<TrustChecker>) Class.forName("org.onap.aaf.cadi.aaf.v2_0.AAFTrustChecker");
149 Constructor<TrustChecker> contc = ctc.getConstructor(Access.class);
151 tc = contc.newInstance(access);
154 } catch (Exception e) {
155 access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
159 Class<Filter> cf=null;
161 cf= (Class<Filter>) Class.forName("org.onap.aaf.cadi.oauth.OAuthFilter");
162 sideChain.add(cf.newInstance());
163 } catch (ClassNotFoundException e) {
164 access.log(Level.DEBUG, "OAuthFilter not enabled");
166 } catch (Exception e) {
167 access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
171 // Synchronize, because some instantiations call init several times on the same object
172 // In this case, the epiTaf will be changed to a non-NullTaf, and thus not instantiate twice.
173 synchronized(CadiHTTPManip.noAdditional /*will always remain same Object*/) {
175 if (httpChecker == null) {
177 access = new PropAccess();
180 httpChecker = new CadiHTTPManip(access,null /*reuseable Con*/,tc, additionalTafLurs);
181 } catch (CadiException | LocatorException e1) {
182 throw new ServletException(e1);
184 } else if (access==null) {
185 access= httpChecker.getAccess();
189 * Setup Authn Path Exceptions
191 if (pathExceptions==null) {
192 String str = getter.get(Config.CADI_NOAUTHN, null, true);
194 pathExceptions = str.split("\\s*:\\s*");
199 * SETUP Permission Converters... those that can take Strings from a Vendor Product, and convert to appropriate AAF Permissions
201 if (mapPairs==null) {
202 String str = getter.get(Config.AAF_PERM_MAP, null, true);
204 String mstr = getter.get(Config.AAF_PERM_MAP, null, true);
206 String map[] = mstr.split("\\s*:\\s*");
208 MapPermConverter mpc=null;
210 mapPairs = new ArrayList<>();
211 for (String entry : map) {
212 if ((idx=entry.indexOf('='))<0) { // it's a Path, so create a new converter
213 access.log(Level.INIT,"Loading Perm Conversions for:",entry);
214 mapPairs.add(new Pair(entry,mpc=new MapPermConverter()));
217 mpc.map().put(entry.substring(0,idx),entry.substring(idx+1));
219 access.log(Level.ERROR,"cadi_perm_map is malformed; ",entry, "is skipped");
229 // Add API Enforcement Point
230 String enforce = getter.get(Config.CADI_API_ENFORCEMENT, null, true);
231 if(enforce!=null && enforce.length()>0) {
232 sideChain.add(new CadiApiEnforcementFilter(access,enforce));
239 * Containers call "destroy" when time to cleanup
241 public void destroy() {
242 // Synchronize, in case multiCadiFilters are used.
243 synchronized(CadiHTTPManip.noAdditional) {
244 if (--count<=0 && httpChecker!=null) {
245 httpChecker.destroy();
256 * This is the standard J2EE invocation. Analyze the request, modify response as necessary, and
257 * only call the next item in the filterChain if request is suitably Authenticated.
259 //TODO Always validate changes against Tomcat AbsCadiValve and Jaspi CadiSAM functions
260 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
261 final long startAll = System.nanoTime();
262 long startCode, startValidate;
263 float code=0f, validate=0f;
266 TafResp tresp = null;
268 HttpServletRequest hreq = (HttpServletRequest)request;
270 startCode=System.nanoTime();
271 chain.doFilter(request, response);
272 code = Timing.millis(startCode);
274 HttpServletResponse hresp = (HttpServletResponse)response;
275 startValidate=System.nanoTime();
276 tresp = httpChecker.validate(hreq, hresp, hreq);
277 validate = Timing.millis(startValidate);
278 if (tresp.isAuthenticated()==RESP.IS_AUTHENTICATED) {
279 user = tresp.getPrincipal().personalName();
280 tag = tresp.getPrincipal().tag();
281 CadiWrap cw = new CadiWrap(hreq, tresp, httpChecker.getLur(),getConverter(hreq));
282 if (httpChecker.notCadi(cw, hresp)) {
283 startCode=System.nanoTime();
284 sideChain.doFilter(cw,response,chain);
285 code = Timing.millis(startCode);
289 } catch (ClassCastException e) {
290 throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e);
293 access.printf(Level.INFO, "Trans: user=%s[%s],ip=%s,ms=%f,validate=%f,code=%f,result=%s",
294 user,tag,request.getRemoteAddr(),
295 Timing.millis(startAll),validate,code,tresp.isAuthenticated().toString());
297 access.printf(Level.INFO, "Trans: user=%s[%s],ip=%s,ms=%f,validate=%f,code=%f,result=FAIL",
298 user,tag,request.getRemoteAddr(),
299 Timing.millis(startAll),validate,code);
306 * If PathExceptions exist, report if these should not have Authn applied.
310 private boolean noAuthn(HttpServletRequest hreq) {
311 if (pathExceptions!=null) {
312 String pi = hreq.getPathInfo();
314 // Attempt to get from URI only (Daniel Rose)
315 pi = hreq.getRequestURI().substring(hreq.getContextPath().length());
318 return false; // JBoss sometimes leaves null
321 for (String pe : pathExceptions) {
322 if (pi.startsWith(pe))return true;
329 * Get Converter by Path
331 private PermConverter getConverter(HttpServletRequest hreq) {
332 if (mapPairs!=null) {
333 String pi = hreq.getPathInfo();
335 for (Pair p: mapPairs) {
336 if (pi.startsWith(p.name))return p.pc;
340 return NullPermConverter.singleton();
344 * store PermConverters by Path prefix
349 public Pair(String key, PermConverter pc) {
354 public PermConverter pc;