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