2 * ============LICENSE_START====================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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====================================================
22 package org.onap.aaf.cadi.aaf.v2_0;
24 import java.io.IOException;
25 import java.security.Principal;
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
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;
57 public class AAFTaf<CLIENT> extends AbsUserCache<AAFPermission> implements HttpTaf {
58 private AAFCon<CLIENT> aaf;
60 private MapBathConverter mapIds;
62 public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning) {
63 super(con.access,con.cleanInterval,con.highCount, con.usageRefreshTriggerCount);
66 initMapBathConverter();
69 public AAFTaf(AAFCon<CLIENT> con, boolean turnOnWarning, AbsUserCache<AAFPermission> other) {
73 initMapBathConverter();
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);
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);
89 private void initMapBathConverter() {
90 String csvFile = access.getProperty(Config.CADI_BATH_CONVERT, null);
95 mapIds = new MapBathConverter(access, new CSV(access,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)");
104 public TafResp validate(final LifeForm reading, final HttpServletRequest req, final HttpServletResponse resp) {
105 //TODO Do we allow just anybody to validate?
107 // Note: Either Carbon or Silicon based LifeForms ok
108 String authz = req.getHeader("Authorization");
109 String target = "invalid";
110 if (authz != null && authz.startsWith("Basic ")) {
111 if (warn&&!req.isSecure()) {
112 aaf.access.log(Level.WARN,"WARNING! BasicAuth has been used over an insecure channel");
115 authz = mapIds.convert(access, authz);
119 final CachedBasicPrincipal bp;
120 if (req.getUserPrincipal() instanceof CachedBasicPrincipal) {
121 bp = (CachedBasicPrincipal)req.getUserPrincipal();
123 bp = new CachedBasicPrincipal(this,authz,aaf.getRealm(),aaf.userExpires);
126 final User<AAFPermission> usr = getUser(bp);
128 && usr.principal instanceof GetCred
129 && Hash.isEqual(bp.getCred(),((GetCred)usr.principal).getCred())) {
130 return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by cached AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false);
133 Miss miss = missed(bp.getName(), bp.getCred());
134 if (miss!=null && !miss.mayContinue()) {
135 return new BasicHttpTafResp(aaf.access,bp.getName(),buildMsg(bp,req,
136 "User/Pass Retry limit exceeded"),
137 RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true);
140 return aaf.bestForUser(
143 public <CL> SecuritySetter<CL> get(AAFCon<CL> con) throws CadiException {
144 return con.basicAuthSS(bp);
146 },new Retryable<BasicHttpTafResp>() {
148 public BasicHttpTafResp code(Rcli<?> client) throws CadiException, APIException {
149 Future<String> fp = client.read("/authn/basicAuth", "text/plain");
150 if (fp.get(aaf.timeout)) {
154 addUser(new User<AAFPermission>(bp,aaf.userExpires));
156 return new BasicHttpTafResp(aaf.access,bp,bp.getName()+" authenticated by AAF password",RESP.IS_AUTHENTICATED,resp,aaf.getRealm(),false);
158 // Note: AddMiss checks for miss==null, and is part of logic
159 boolean rv= addMiss(bp.getName(),bp.getCred());
161 return new BasicHttpTafResp(aaf.access,bp.getName(),buildMsg(bp,req,
162 "user/pass combo invalid via AAF from " + req.getRemoteAddr()),
163 RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),true);
165 return new BasicHttpTafResp(aaf.access,bp.getName(),buildMsg(bp,req,
166 "user/pass combo invalid via AAF from " + req.getRemoteAddr() + " - Retry limit exceeded"),
167 RESP.FAIL,resp,aaf.getRealm(),true);
173 } catch (IOException e) {
174 String msg = buildMsg(null,req,"Invalid Auth Token");
175 aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')');
176 return new BasicHttpTafResp(aaf.access,target,msg, RESP.TRY_AUTHENTICATING, resp, aaf.getRealm(),true);
177 } catch (Exception e) {
178 String msg = buildMsg(null,req,"Authenticating Service unavailable");
181 } catch (CadiException e1) {
182 aaf.access.log(e1, "Error Invalidating Client");
184 aaf.access.log(Level.WARN,msg,'(', e.getMessage(), ')');
185 return new BasicHttpTafResp(aaf.access,target,msg, RESP.FAIL, resp, aaf.getRealm(),false);
188 return new BasicHttpTafResp(aaf.access,target,"Requesting HTTP Basic Authorization",RESP.TRY_AUTHENTICATING,resp,aaf.getRealm(),false);
191 private String buildMsg(Principal pr, HttpServletRequest req, Object... msg) {
192 StringBuilder sb = new StringBuilder();
193 for (Object s : msg) {
194 sb.append(s.toString());
198 sb.append(pr.getName());
201 sb.append(req.getRemoteAddr());
203 sb.append(req.getRemotePort());
204 return sb.toString();
209 public Resp revalidate(CachedPrincipal prin, Object state) {
210 // !!!! TEST THIS.. Things may not be revalidated, if not BasicPrincipal
211 if (prin instanceof BasicPrincipal) {
214 Rcli<CLIENT> userAAF = aaf.client().forUser(aaf.transferSS((BasicPrincipal)prin));
215 fp = userAAF.read("/authn/basicAuth", "text/plain");
216 return fp.get(aaf.timeout)?Resp.REVALIDATED:Resp.UNVALIDATED;
217 } catch (Exception e) {
218 aaf.access.log(e, "Cannot Revalidate",prin.getName());
219 return Resp.INACCESSIBLE;
222 return Resp.NOT_MINE;