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