6334164e7f74533430b74ecd4f64133347f0cbe2
[aaf/authz.git] / cadi / core / src / main / java / org / onap / aaf / cadi / taf / HttpEpiTaf.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.taf;
23
24 import java.net.URI;
25 import java.security.Principal;
26 import java.util.ArrayList;
27 import java.util.List;
28
29 import javax.servlet.http.HttpServletRequest;
30 import javax.servlet.http.HttpServletResponse;
31
32 import org.onap.aaf.cadi.Access;
33 import org.onap.aaf.cadi.Access.Level;
34 import org.onap.aaf.cadi.CachedPrincipal;
35 import org.onap.aaf.cadi.CachedPrincipal.Resp;
36 import org.onap.aaf.cadi.CadiException;
37 import org.onap.aaf.cadi.Locator;
38 import org.onap.aaf.cadi.Taf.LifeForm;
39 import org.onap.aaf.cadi.TrustChecker;
40
41 /**
42  * HttpEpiTaf
43  *
44  * An extension of the basic "EpiTAF" concept, check known HTTP Related TAFs for valid credentials
45  *
46  * @author Jonathan
47  *
48  */
49 public class HttpEpiTaf implements HttpTaf {
50     private HttpTaf[] tafs;
51     private Access access;
52     private Locator<URI> locator;
53     private TrustChecker trustChecker;
54
55     /**
56      * HttpEpiTaf constructor
57      *
58      * Construct the HttpEpiTaf from variable Http specific TAF parameters
59
60      * @param tafs
61      * @throws CadiException
62      */
63     public HttpEpiTaf(Access access, Locator<URI> locator, TrustChecker tc, HttpTaf ... tafs) throws CadiException{
64         this.tafs = tafs;
65         this.access = access;
66         this.locator = locator;
67         this.trustChecker = tc;
68         // Establish what Header Property to look for UserChain/Trust Props
69
70         if (tafs.length == 0) {
71             throw new CadiException("Need at least one HttpTaf implementation in constructor");
72         }
73     }
74
75     /**
76      * validate
77      *
78      * Respond with the first Http specific TAF to authenticate user based on variable info
79      * and "LifeForm" (is it a human behind a browser, or a server utilizing HTTP Protocol).
80      *
81      * If there is no HttpTAF that can authenticate, respond with the first TAF that suggests it can
82      * establish an Authentication conversation (TRY_AUTHENTICATING) (Examples include a redirect to CSP
83      * Servers for CSP Cookie, or BasicAuth 401 response, suggesting User/Password for given Realm
84      * submission
85      *
86      * If no TAF declares either, respond with NullTafResp (which denies all questions)
87      */
88     public TafResp validate(LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
89         // Given a LifeForm Neutral, for HTTP, we need to discover true Life-Form Readings
90         if (reading == LifeForm.LFN) {
91             reading = tricorderScan(req);
92         }
93         TafResp tresp = null;
94         TafResp firstTry = null;
95         List<Redirectable> redirectables = null;
96         List<TafResp> log;
97         if (access.willLog(Level.DEBUG)) {
98             log = new ArrayList<>();
99         } else {
100             log = null;
101         }
102         try {
103             for (HttpTaf taf : tafs) {
104                 final long start = System.nanoTime();
105                 tresp = taf.validate(reading, req, resp);
106                 addToLog(log, tresp, start);
107                 switch(tresp.isAuthenticated()) {
108                     case TRY_ANOTHER_TAF:
109                         break; // and loop
110                     case TRY_AUTHENTICATING:
111                         if (tresp instanceof Redirectable) {
112                             if (redirectables == null) {
113                                 redirectables = new ArrayList<>();
114                             }
115                             redirectables.add((Redirectable)tresp);
116                         } else if (firstTry == null) {
117                             firstTry = tresp;
118                         }
119                         break;
120                     case IS_AUTHENTICATED:
121                         tresp = trustChecker.mayTrust(tresp, req);
122                         return tresp;
123                     default:
124                         return tresp;
125                 }
126             }
127         } finally {
128             printLog(log);
129         }
130
131         // If No TAFs configured, at this point.  It is safer at this point to be "not validated",
132         // rather than "let it go"
133         // Note: if exists, there will always be more than 0 entries, according to above code
134         if (redirectables == null) {
135             return (firstTry != null) ? firstTry : NullTafResp.singleton();
136         }
137
138         // If there is one Tryable entry then return it
139         if (redirectables.size() > 1) {
140             return LoginPageTafResp.create(access, locator, resp, redirectables);
141         } else {
142             return redirectables.get(0);
143         }
144     }
145
146     public boolean revalidate(Principal prin) throws Exception {
147         return false;
148     }
149
150     /*
151      * Since this is internal, we use a little Star Trek humor to indicate looking in the HTTP Request to see if we can determine what kind
152      * of "LifeForm" reading we can determine, i.e. is there a Human (CarbonBasedLifeForm) behind a browser, or is it mechanical
153      * id (SiliconBasedLifeForm)?  This makes a difference in some Authentication, i.e CSP, which doesn't work well for SBLFs
154      */
155     private LifeForm tricorderScan(HttpServletRequest req) {
156         // For simplicity's sake, we'll say Humans use FQDNs, not IPs.
157
158         // Current guess that only Browsers bother to set "Agent" codes that identify the kind of browser they are.
159         // If mechanical frameworks are found that populate this, then more advanced analysis may be required
160         // Jonathan 1/22/2013
161         String agent = req.getHeader("User-Agent");
162         if (agent != null && agent.startsWith("Mozilla")) { // covers I.E./Firefox/Safari/probably any other "advanced" Browser see http://en.wikipedia.org/wiki/User_agent
163             return LifeForm.CBLF;
164         }
165         return LifeForm.SBLF;                            // notably skips "curl","wget", (which is desired behavior.  We don't want to try CSP, etc on these)
166     }
167
168     public Resp revalidate(CachedPrincipal prin, Object state) {
169         Resp resp;
170         for (HttpTaf taf : tafs) {
171             resp = taf.revalidate(prin, state);
172             if (resp != Resp.NOT_MINE) {
173                 return resp;
174             }
175 //            switch(resp) {
176 //                case NOT_MINE:
177 //                    break;
178 //                default:
179 //                    return resp;
180 //            }
181         }
182         return Resp.NOT_MINE;
183     }
184     
185     private void addToLog(List<TafResp> log, final TafResp tresp, final long start) {
186         if (log == null) {
187             return;
188         }
189         tresp.timing(start);
190         log.add(tresp);
191     }
192     
193     private void printLog(List<TafResp> log) {
194         if (log == null) {
195             return;
196         }
197         for (TafResp tresp : log) {
198             access.printf(Level.DEBUG, "%s: %s, ms=%f", tresp.taf(), tresp.desc(), tresp.timing());
199         }
200     }
201
202     /**
203      * List HttpTafs with their "toString" representations... primarily useful for Debugging in an IDE
204      * like Eclipse.
205      */
206     public String toString() {
207         StringBuilder sb = new StringBuilder();
208         for (HttpTaf ht : tafs) {
209             sb.append(ht.toString());
210             sb.append(". ");
211         }
212         return sb.toString();
213     }
214 }