ff0c246bfcf235ad3f6e60020155dc53cbc184ba
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / obasic / OBasicHttpTaf.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.obasic;
23
24 import java.io.IOException;
25 import java.security.NoSuchAlgorithmException;
26 import java.security.Principal;
27
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.onap.aaf.cadi.BasicCred;
32 import org.onap.aaf.cadi.CachedPrincipal;
33 import org.onap.aaf.cadi.CadiException;
34 import org.onap.aaf.cadi.CredVal;
35 import org.onap.aaf.cadi.Hash;
36 import org.onap.aaf.cadi.LocatorException;
37 import org.onap.aaf.cadi.PropAccess;
38 import org.onap.aaf.cadi.Symm;
39 import org.onap.aaf.cadi.Taf;
40 import org.onap.aaf.cadi.Access.Level;
41 import org.onap.aaf.cadi.CachedPrincipal.Resp;
42 import org.onap.aaf.cadi.CredVal.Type;
43 import org.onap.aaf.cadi.client.Result;
44 import org.onap.aaf.cadi.oauth.AbsOTafLur;
45 import org.onap.aaf.cadi.oauth.OAuth2Principal;
46 import org.onap.aaf.cadi.oauth.TimedToken;
47 import org.onap.aaf.cadi.oauth.TokenClient;
48 import org.onap.aaf.cadi.taf.HttpTaf;
49 import org.onap.aaf.cadi.taf.TafResp;
50 import org.onap.aaf.cadi.taf.TafResp.RESP;
51 import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp;
52 import org.onap.aaf.cadi.util.FQI;
53 import org.onap.aaf.misc.env.APIException;
54 import org.onap.aaf.misc.env.util.Pool.Pooled;
55
56 /**
57  * BasicHttpTaf
58  * 
59  * This TAF implements the "Basic Auth" protocol.  
60  * 
61  * WARNING! It is true for any implementation of "Basic Auth" that the password is passed unencrypted.  
62  * This is because the expectation, when designed years ago, was that it would only be used in 
63  * conjunction with SSL (https).  It is common, however, for users to ignore this on the assumption that
64  * their internal network is secure, or just ignorance.  Therefore, a WARNING will be printed
65  * when the HTTP Channel is not encrypted (unless explicitly turned off).
66  * 
67  * @author Jonathan
68  *
69  */
70 public class OBasicHttpTaf extends AbsOTafLur implements HttpTaf {
71         private final String realm;
72         private final CredVal rbac;
73         
74         
75         public OBasicHttpTaf(final PropAccess access, final CredVal rbac, final String realm, final String token_url, final String introspect_url) throws CadiException {
76                 super(access, token_url,introspect_url);
77                 this.rbac = rbac;
78                 this.realm = realm;
79         }
80         
81         /**
82          * Note: BasicHttp works for either Carbon Based (Humans) or Silicon Based (machine) Lifeforms.  
83          * @see Taf
84          */
85         public TafResp validate(Taf.LifeForm reading, HttpServletRequest req, HttpServletResponse resp) {
86                 // See if Request implements BasicCred (aka CadiWrap or other), and if User/Pass has already been set separately
87                 final String user;
88                 String password=null;
89                 byte[] cred=null;
90                 if(req instanceof BasicCred) {
91                         BasicCred bc = (BasicCred)req;
92                         user = bc.getUser();
93                         cred = bc.getCred();
94                 } else {
95                         String authz = req.getHeader("Authorization");
96                         if(authz != null && authz.startsWith("Basic ")) {
97                                 if(!req.isSecure()) {
98                                         access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
99                                 }
100                                 try {
101                                         String temp = Symm.base64noSplit.decode(authz.substring(6));
102                                         int colon = temp.lastIndexOf(':');
103                                         if(colon>0) {
104                                                 user = temp.substring(0,colon);
105                                                 password = temp.substring(colon+1);
106                                         } else {
107                                                 access.printf(Level.AUDIT,"Malformed BasicAuth entry ip=%s, entry=%s",req.getRemoteAddr(),
108                                                                 access.encrypt(temp));
109                                                 return new BasicHttpTafResp(access,null,"Malformed BasicAuth entry",RESP.FAIL,resp,realm,false);
110                                         }
111                                         if(!rbac.validate(user,Type.PASSWORD,password.getBytes(),req)) {
112                                                 return new BasicHttpTafResp(access,null,buildMsg(null,req,"user/pass combo invalid for ",user,"from",req.getRemoteAddr()), 
113                                                                 RESP.TRY_AUTHENTICATING,resp,realm,true);
114                                         }
115                                 } catch (IOException e) {
116                                         access.log(e, ERROR_GETTING_TOKEN_CLIENT);
117                                         return new BasicHttpTafResp(access,null,ERROR_GETTING_TOKEN_CLIENT,RESP.FAIL,resp,realm,false);
118                                 }
119                         } else {
120                                 return new BasicHttpTafResp(access,null,"Not a Basic Auth",RESP.TRY_ANOTHER_TAF,resp,realm,false);
121                         }
122                 }
123
124                 try {
125                         if(password==null && cred!=null) {
126                                 password = new String(cred);
127                                 cred = Hash.hashSHA256(cred);
128                         } else if(password!=null && cred==null) {
129                                 cred = Hash.hashSHA256(password.getBytes());
130                         }
131                         Pooled<TokenClient> pclient = tokenClientPool.get();
132                         try {
133                                 pclient.content.password(user, password);
134                                 String scope=FQI.reverseDomain(client_id);
135                                 Result<TimedToken> rtt = pclient.content.getToken('B',scope);
136                                 if(rtt.isOK()) {
137                                         if(rtt.value.expired()) {
138                                                 return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: Token Expired",RESP.FAIL,resp,realm,true);
139                                         } else {
140                                                 TimedToken tt = rtt.value;
141                                                 Result<OAuth2Principal> prin = tkMgr.toPrincipal(tt.getAccessToken(), cred);
142                                                 if(prin.isOK()) {
143                                                         return new BasicHttpTafResp(access,prin.value,"BasicAuth/OAuth Token Authentication",RESP.IS_AUTHENTICATED,resp,realm,true);
144                                                 } else {
145                                                         return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: " + prin.code + ' ' + prin.error,RESP.FAIL,resp,realm,true);
146                                                 }
147                                         }
148                                 } else {
149                                         return new BasicHttpTafResp(access,null,"BasicAuth/OAuth Token: " + rtt.code + ' ' + rtt.error,RESP.FAIL,resp,realm,true);
150                                 }
151                         } finally {
152                                 pclient.done();
153                         }                               
154                 } catch (APIException | CadiException | LocatorException | NoSuchAlgorithmException e) {
155                         access.log(e, ERROR_GETTING_TOKEN_CLIENT);
156                         return new BasicHttpTafResp(access,null,ERROR_GETTING_TOKEN_CLIENT,RESP.TRY_ANOTHER_TAF,resp,realm,false);
157                 }
158         }
159         
160         protected String buildMsg(Principal pr, HttpServletRequest req, Object ... msg) {
161                 StringBuilder sb = new StringBuilder();
162                 if(pr!=null) {
163                         sb.append("user=");
164                         sb.append(pr.getName());
165                         sb.append(',');
166                 }
167                 sb.append("ip=");
168                 sb.append(req.getRemoteAddr());
169                 sb.append(",port=");
170                 sb.append(req.getRemotePort());
171                 if(msg.length>0) {
172                         sb.append(",msg=\"");
173                         for(Object s : msg) {
174                                 sb.append(s.toString());
175                         }
176                         sb.append('"');
177                 }
178                 return sb.toString();
179         }
180
181         @Override
182         public Resp revalidate(CachedPrincipal prin, Object state) {
183 //              if(prin instanceof BasicPrincipal) {
184 //                      BasicPrincipal ba = (BasicPrincipal)prin;
185 //                      if(DenialOfServiceTaf.isDeniedID(ba.getName())!=null) {
186 //                              return Resp.UNVALIDATED;
187 //                      }
188 //                      return rbac.validate(ba.getName(), Type.PASSWORD, ba.getCred(), state)?Resp.REVALIDATED:Resp.UNVALIDATED;
189 //              }
190                 return Resp.NOT_MINE;
191         }
192         
193         public String toString() {
194                 return "Basic Auth enabled on realm: " + realm;
195         }
196 }