1 /*******************************************************************************
\r
2 * ============LICENSE_START====================================================
\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
11 * * http://www.apache.org/licenses/LICENSE-2.0
\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
20 * * ECOMP is a trademark and service mark of AT&T Intellectual Property.
\r
22 ******************************************************************************/
\r
23 package org.onap.aaf.cadi.filter;
\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
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
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
55 * This class implements Servlet Filter, and ties together CADI implementations
\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
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
74 public Lur getLur() {
\r
75 return httpChecker.getLur();
\r
79 * Construct a viable Filter
\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
86 public CadiFilter() {
\r
87 additionalTafLurs = CadiHTTPManip.noAdditional;
\r
91 * This constructor to be used when directly constructing and placing in HTTP Engine
\r
94 * @param moreTafLurs
\r
95 * @throws ServletException
\r
97 public CadiFilter(Access access, Object ... moreTafLurs) throws ServletException {
\r
98 additionalTafLurs = moreTafLurs;
\r
99 init(new AccessGetter(this.access = access));
\r
104 * Use this to pass in a PreContructed CADI Filter, but with initializing... let Servlet do it
\r
107 * @param moreTafLurs
\r
108 * @throws ServletException
\r
110 public CadiFilter(boolean init, PropAccess access, Object ... moreTafLurs) throws ServletException {
\r
111 this.access = access;
\r
113 init(new AccessGetter(access));
\r
115 additionalTafLurs = moreTafLurs;
\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
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
133 // Set Protected getter with base Access, for internal class instantiations
\r
134 init(new FCGet(access, sca.context(), filterConfig));
\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
142 @SuppressWarnings("unchecked")
\r
143 Class<TrustChecker> ctc = (Class<TrustChecker>) Class.forName("com.att.cadi.aaf.v2_0.AAFTrustChecker");
\r
145 Constructor<TrustChecker> contc = ctc.getConstructor(Access.class);
\r
147 tc = contc.newInstance(access);
\r
150 } catch (Exception e) {
\r
151 access.log(Level.INIT, "AAFTrustChecker cannot be loaded",e.getMessage());
\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
159 if(httpChecker == null) {
\r
161 access = new PropAccess();
\r
164 httpChecker = new CadiHTTPManip(access,null /*reuseable Con*/,tc, additionalTafLurs);
\r
165 } catch (CadiException e1) {
\r
166 throw new ServletException(e1);
\r
168 } else if(access==null) {
\r
169 access= httpChecker.getAccess();
\r
173 * Setup Authn Path Exceptions
\r
175 if(pathExceptions==null) {
\r
176 String str = getter.get(Config.CADI_NOAUTHN, null, true);
\r
178 pathExceptions = str.split("\\s*:\\s*");
\r
183 * SETUP Permission Converters... those that can take Strings from a Vendor Product, and convert to appropriate AAF Permissions
\r
185 if(mapPairs==null) {
\r
186 String str = getter.get(Config.AAF_PERM_MAP, null, true);
\r
188 String mstr = getter.get(Config.AAF_PERM_MAP, null, true);
\r
190 String map[] = mstr.split("\\s*:\\s*");
\r
192 MapPermConverter mpc=null;
\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
201 mpc.map().put(entry.substring(0,idx),entry.substring(idx+1));
\r
203 access.log(Level.ERROR,"cadi_perm_map is malformed; ",entry, "is skipped");
\r
218 * Containers call "destroy" when time to cleanup
\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
227 pathExceptions=null;
\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
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
241 HttpServletRequest hreq = (HttpServletRequest)request;
\r
242 if(noAuthn(hreq)) {
\r
243 chain.doFilter(request, response);
\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
254 } catch (ClassCastException e) {
\r
255 throw new ServletException("CadiFilter expects Servlet to be an HTTP Servlet",e);
\r
261 * If PathExceptions exist, report if these should not have Authn applied.
\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
277 * Get Converter by Path
\r
279 private PermConverter getConverter(HttpServletRequest hreq) {
\r
280 if(mapPairs!=null) {
\r
281 String pi = hreq.getPathInfo();
\r
283 for(Pair p: mapPairs) {
\r
284 if(pi.startsWith(p.name))return p.pc;
\r
288 return NullPermConverter.singleton();
\r
292 * store PermConverters by Path prefix
\r
295 private class Pair {
\r
296 public Pair(String key, PermConverter pc) {
\r
300 public String name;
\r
301 public PermConverter pc;
\r