ea404400cad4e496ef9f6b0c9a18cd5d9109e17e
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / aaf / v2_0 / AAFTaf.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.aaf.v2_0;
23
24 import java.io.IOException;
25 import java.security.Principal;
26
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.onap.aaf.cadi.AbsUserCache;
31 import org.onap.aaf.cadi.Access.Level;
32 import org.onap.aaf.cadi.CachedPrincipal;
33 import org.onap.aaf.cadi.CachedPrincipal.Resp;
34 import org.onap.aaf.cadi.CadiException;
35 import org.onap.aaf.cadi.Connector;
36 import org.onap.aaf.cadi.GetCred;
37 import org.onap.aaf.cadi.Hash;
38 import org.onap.aaf.cadi.SecuritySetter;
39 import org.onap.aaf.cadi.Taf.LifeForm;
40 import org.onap.aaf.cadi.User;
41 import org.onap.aaf.cadi.aaf.AAFPermission;
42 import org.onap.aaf.cadi.aaf.v2_0.AAFCon.GetSetter;
43 import org.onap.aaf.cadi.client.Future;
44 import org.onap.aaf.cadi.client.Rcli;
45 import org.onap.aaf.cadi.client.Retryable;
46 import org.onap.aaf.cadi.config.Config;
47 import org.onap.aaf.cadi.filter.MapBathConverter;
48 import org.onap.aaf.cadi.principal.BasicPrincipal;
49 import org.onap.aaf.cadi.principal.CachedBasicPrincipal;
50 import org.onap.aaf.cadi.taf.HttpTaf;
51 import org.onap.aaf.cadi.taf.TafResp;
52 import org.onap.aaf.cadi.taf.TafResp.RESP;
53 import org.onap.aaf.cadi.taf.basic.BasicHttpTafResp;
54 import org.onap.aaf.cadi.util.CSV;
55 import org.onap.aaf.misc.env.APIException;
56
57 public class AAFTaf<CLIENT> extends AbsUserCache<AAFPermission> implements HttpTaf {
58     private AAFCon<CLIENT> aaf;
59     private boolean warn;
60     private MapBathConverter mapIds;
61     
62     public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning) {
63         super(con.access,con.cleanInterval,con.highCount, con.usageRefreshTriggerCount);
64         aaf = con;
65         warn = turnOnWarning;
66         initMapBathConverter();
67     }
68
69     public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning, AbsUserCache<AAFPermission> other) {
70         super(other);
71         aaf = con;
72         warn = turnOnWarning;
73         initMapBathConverter();
74
75     }
76     
77     // Note: Needed for Creation of this Object with Generics
78     @SuppressWarnings("unchecked")
79     public AAFTaf(Connector mustBeAAFCon, boolean turnOnWarning, AbsUserCache<AAFPermission> other) {
80         this((AAFCon<CLIENT>)mustBeAAFCon,turnOnWarning,other);
81     }
82
83     // Note: Needed for Creation of this Object with Generics
84     @SuppressWarnings("unchecked")
85     public AAFTaf(Connector mustBeAAFCon, boolean turnOnWarning) {
86         this((AAFCon<CLIENT>)mustBeAAFCon,turnOnWarning);
87     }
88
89     private void initMapBathConverter() {
90         String csvFile = access.getProperty(Config.CADI_BATH_CONVERT, null);
91         if(csvFile==null) {
92                 mapIds=null;
93         } else {
94                 try {
95                                 mapIds = new MapBathConverter(access, new CSV(csvFile));
96                                 access.log(Level.INIT,"Basic Auth Conversion using",csvFile,"enabled" );
97                         } catch (IOException | CadiException e) {
98                                 access.log(e,"Bath Map Conversion is not initialized (non fatal)");
99                         }
100         }
101
102     }
103
104     public TafResp validate(final LifeForm reading, final HttpServletRequest req, final HttpServletResponse resp) {
105         //TODO Do we allow just anybody to validate?
106
107         // Note: Either Carbon or Silicon based LifeForms ok
108         String authz = req.getHeader("Authorization");
109         if (authz != null && authz.startsWith("Basic ")) {
110             if (warn&&!req.isSecure()) {
111                 aaf.access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
112             }
113             if(mapIds != null) {
114                 authz = mapIds.convert(access, authz);
115             }
116
117             try {
118                 final CachedBasicPrincipal bp;
119                 if (req.getUserPrincipal() instanceof CachedBasicPrincipal) {
120                     bp = (CachedBasicPrincipal)req.getUserPrincipal();
121                 } else {
122                     bp = new CachedBasicPrincipal(this,authz,aaf.getRealm(),aaf.userExpires);
123                 }
124                 // First try Cache
125                 final User<AAFPermission> usr = getUser(bp);
126                 if (usr != null
127                     && usr.principal instanceof GetCred
128                     && Hash.isEqual(bp.getCred(),((GetCred)usr.principal).getCred())) {
129                     return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by cached AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false);
130                 }
131
132                 Miss miss = missed(bp.getName(), bp.getCred());
133                 if (miss!=null && !miss.mayContinue()) {
134                     return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req,
135                             "User/Pass Retry limit exceeded"), 
136                             RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true);
137                 }
138                 
139                 return aaf.bestForUser(
140                     new GetSetter() {
141                         @Override
142                         public <CL> SecuritySetter<CL> get(AAFCon<CL> con) throws CadiException {
143                             return con.basicAuthSS(bp);
144                         }
145                     },new Retryable<BasicHttpTafResp>() {
146                         @Override
147                         public BasicHttpTafResp code(Rcli<?> client) throws CadiException, APIException {
148                             Future<String> fp = client.read("/authn/basicAuth", "text/plain");
149                             if (fp.get(aaf.timeout)) {
150                                 if (usr!=null) {
151                                     usr.principal = bp;
152                                 } else {
153                                     addUser(new User<AAFPermission>(bp,aaf.userExpires));
154                                 }
155                                 return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false);
156                             } else {
157                                 // Note: AddMiss checks for miss==null, and is part of logic
158                                 boolean rv= addMiss(bp.getName(),bp.getCred());
159                                 if (rv) {
160                                     return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req,
161                                             "user/pass combo invalid via AAF from " + req.getRemoteAddr()), 
162                                             RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true);
163                                 } else {
164                                     return new BasicHttpTafResp(aaf.access,null,buildMsg(bp,req,
165                                             "user/pass combo invalid via AAF from " + req.getRemoteAddr() + " - Retry limit exceeded"), 
166                                             RESP.FAIL,resp,aaf.getRealm(),true);
167                                 }
168                             }
169                         }
170                     }
171                 );
172             } catch (IOException e) {
173                 String msg = buildMsg(null,req,"Invalid Auth Token");
174                 aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')');
175                 return new BasicHttpTafResp(aaf.access,null,msg, RESP.TRY_AUTHENTICATING, resp, aaf.getRealm(),true);
176             } catch (Exception e) {
177                 String msg = buildMsg(null,req,"Authenticating Service unavailable");
178                 try {
179                     aaf.invalidate();
180                 } catch (CadiException e1) {
181                     aaf.access.log(e1, "Error Invalidating Client");
182                 }
183                 aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')');
184                 return new BasicHttpTafResp(aaf.access,null,msg, RESP.FAIL, resp, aaf.getRealm(),false);
185             }
186         }
187         return new BasicHttpTafResp(aaf.access,null,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),false);
188     }
189     
190     private String buildMsg(Principal pr, HttpServletRequest req, Object... msg) {
191         StringBuilder sb = new StringBuilder();
192         for (Object s : msg) {
193             sb.append(s.toString());
194         }
195         if (pr!=null) {
196             sb.append(" for ");
197             sb.append(pr.getName());
198         }
199         sb.append(" from ");
200         sb.append(req.getRemoteAddr());
201         sb.append(':');
202         sb.append(req.getRemotePort());
203         return sb.toString();
204     }
205
206
207     
208     public Resp revalidate(CachedPrincipal prin, Object state) {
209         //  !!!! TEST THIS.. Things may not be revalidated, if not BasicPrincipal
210         if (prin instanceof BasicPrincipal) {
211             Future<String> fp;
212             try {
213                 Rcli<CLIENT> userAAF = aaf.client().forUser(aaf.transferSS((BasicPrincipal)prin));
214                 fp = userAAF.read("/authn/basicAuth", "text/plain");
215                 return fp.get(aaf.timeout)?Resp.REVALIDATED:Resp.UNVALIDATED;
216             } catch (Exception e) {
217                 aaf.access.log(e, "Cannot Revalidate",prin.getName());
218                 return Resp.INACCESSIBLE;
219             }
220         }
221         return Resp.NOT_MINE;
222     }
223
224 }