Update for more Logging Info
[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         String user = "invalid";
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,user,"Malformed BasicAuth entry",RESP.FAIL,resp,realm,false);
110                     }
111                     if (!rbac.validate(user,Type.PASSWORD,password.getBytes(),req)) {
112                         return new BasicHttpTafResp(access,user,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,user,ERROR_GETTING_TOKEN_CLIENT,RESP.FAIL,resp,realm,false);
118                 }
119             } else {
120                 return new BasicHttpTafResp(access,user,"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,user,"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,user,"BasicAuth/OAuth Token: " + prin.code + ' ' + prin.error,RESP.FAIL,resp,realm,true);
146                         }
147                     }
148                 } else {
149                     return new BasicHttpTafResp(access,user,"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,user,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 }