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