Fix CADI Connection to Remote DNS AAF servers on proxied network
[aaf/authz.git] / cadi / aaf / src / main / java / org / onap / aaf / cadi / aaf / v2_0 / AAFCon.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.net.URI;
25 import java.net.UnknownHostException;
26 import java.util.Map;
27 import java.util.concurrent.ConcurrentHashMap;
28
29 import org.onap.aaf.cadi.AbsUserCache;
30 import org.onap.aaf.cadi.Access;
31 import org.onap.aaf.cadi.Access.Level;
32 import org.onap.aaf.cadi.CadiException;
33 import org.onap.aaf.cadi.CadiWrap;
34 import org.onap.aaf.cadi.Connector;
35 import org.onap.aaf.cadi.Locator;
36 import org.onap.aaf.cadi.LocatorException;
37 import org.onap.aaf.cadi.Lur;
38 import org.onap.aaf.cadi.PropAccess;
39 import org.onap.aaf.cadi.SecuritySetter;
40 import org.onap.aaf.cadi.aaf.AAFPermission;
41 import org.onap.aaf.cadi.aaf.marshal.CertsMarshal;
42 import org.onap.aaf.cadi.client.Future;
43 import org.onap.aaf.cadi.client.Rcli;
44 import org.onap.aaf.cadi.client.Retryable;
45 import org.onap.aaf.cadi.config.Config;
46 import org.onap.aaf.cadi.config.RegistrationPropHolder;
47 import org.onap.aaf.cadi.config.SecurityInfoC;
48 import org.onap.aaf.cadi.lur.EpiLur;
49 import org.onap.aaf.cadi.principal.BasicPrincipal;
50 import org.onap.aaf.cadi.principal.TaggedPrincipal;
51 import org.onap.aaf.cadi.util.FQI;
52 import org.onap.aaf.cadi.util.Vars;
53 import org.onap.aaf.misc.env.APIException;
54 import org.onap.aaf.misc.env.Data.TYPE;
55 import org.onap.aaf.misc.rosetta.env.RosettaDF;
56 import org.onap.aaf.misc.rosetta.env.RosettaEnv;
57
58 import aaf.v2_0.Certs;
59 import aaf.v2_0.CredRequest;
60 import aaf.v2_0.Error;
61 import aaf.v2_0.Perms;
62 import aaf.v2_0.Users;
63
64 public abstract class AAFCon<CLIENT> implements Connector {
65     final public Access access;
66     // Package access
67     final public int timeout, cleanInterval, connTimeout;
68     final public int highCount, userExpires, usageRefreshTriggerCount;
69     private Map<String,Rcli<CLIENT>> clients = new ConcurrentHashMap<>();
70     final public RosettaDF<Perms> permsDF;
71     final public RosettaDF<Certs> certsDF;
72     final public RosettaDF<Users> usersDF;
73     final public RosettaDF<CredRequest> credReqDF;
74     final public RosettaDF<Error> errDF;
75     private String realm;
76     public final String app;
77     protected final String apiVersion;
78     protected SecurityInfoC<CLIENT> si;
79
80     private AAFLurPerm lur;
81
82     final public RosettaEnv env;
83     protected AAFCon(AAFCon<CLIENT> copy) {
84         access = copy.access;
85         apiVersion = access.getProperty(Config.AAF_API_VERSION, Config.AAF_DEFAULT_API_VERSION);
86         timeout = copy.timeout;
87         cleanInterval = copy.cleanInterval;
88         connTimeout = copy.connTimeout;
89         highCount = copy.highCount;
90         userExpires = copy.userExpires;
91         usageRefreshTriggerCount = copy.usageRefreshTriggerCount;
92         permsDF = copy.permsDF;
93         certsDF = copy.certsDF;
94         usersDF = copy.usersDF;
95         credReqDF = copy.credReqDF;
96         errDF = copy.errDF;
97         app = copy.app;
98         si = copy.si;
99         env = copy.env;
100         realm = copy.realm;
101     }
102     protected AAFCon(Access access, String tag, SecurityInfoC<CLIENT> si) throws CadiException{
103         apiVersion = access.getProperty(Config.AAF_API_VERSION, Config.AAF_DEFAULT_API_VERSION);
104         if (tag==null) {
105             throw new CadiException("AAFCon cannot be constructed without a property tag or URL");
106         } else {
107             String str = access.getProperty(tag,null);
108             if (str==null) {
109                 if (tag.contains("://")) { // assume a URL
110                     str = tag;
111                 } else {
112                     throw new CadiException("A URL or " + tag + " property is required.");
113                 }
114             }
115             try {
116                 RegistrationPropHolder rph = new RegistrationPropHolder(access, 0);
117                 str = rph.replacements("AAFCon",str, null,null);
118             } catch (UnknownHostException e) {
119                 throw new CadiException(e);
120             }
121             access.printf(Level.INFO, "AAFCon has URL of %s",str);
122             setInitURI(str);
123         }
124         try {
125             this.access = access;
126             this.si = si;
127             if (si.defSS.getID().equals(SecurityInfoC.DEF_ID)) { // it's the Preliminary SS, try to get a better one
128                 String mechid = access.getProperty(Config.AAF_APPID, null);
129                 if (mechid==null) {
130                     mechid=access.getProperty(Config.OAUTH_CLIENT_ID,null);
131                 }
132                 String alias = access.getProperty(Config.CADI_ALIAS, null);
133                 if(alias != null) {
134                     si.defSS=x509Alias(alias);
135                     set(si.defSS);
136                 } else {
137
138                     String encpass = access.getProperty(Config.AAF_APPPASS, null);
139                     if (encpass==null) {
140                         encpass = access.getProperty(Config.OAUTH_CLIENT_SECRET,null);
141                     }
142
143                     if (encpass==null) {
144                         if (alias==null) {
145                             access.printf(Access.Level.WARN,"%s, %s or %s required before use.", Config.CADI_ALIAS, Config.AAF_APPID, Config.OAUTH_CLIENT_ID);
146                             set(si.defSS);
147                         }
148                     } else {
149                         if (mechid!=null) {
150                             si.defSS=basicAuth(mechid, encpass);
151                             set(si.defSS);
152                         } else {
153                             si.defSS=new SecuritySetter<CLIENT>() {
154
155                                 @Override
156                                 public String getID() {
157                                     return "";
158                                 }
159
160                                 @Override
161                                 public void setSecurity(CLIENT client) throws CadiException {
162                                     throw new CadiException("AAFCon has not been initialized with Credentials (SecuritySetter)");
163                                 }
164
165                                 @Override
166                                 public int setLastResponse(int respCode) {
167                                     return 0;
168                                 }
169                             };
170                             set(si.defSS);
171                         }
172                     }
173                 }
174             }
175
176             timeout = Integer.parseInt(access.getProperty(Config.AAF_CALL_TIMEOUT, Config.AAF_CALL_TIMEOUT_DEF));
177             cleanInterval = Integer.parseInt(access.getProperty(Config.AAF_CLEAN_INTERVAL, Config.AAF_CLEAN_INTERVAL_DEF));
178             highCount = Integer.parseInt(access.getProperty(Config.AAF_HIGH_COUNT, Config.AAF_HIGH_COUNT_DEF).trim());
179             connTimeout = Integer.parseInt(access.getProperty(Config.AAF_CONN_TIMEOUT, Config.AAF_CONN_TIMEOUT_DEF).trim());
180             userExpires = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim());
181             usageRefreshTriggerCount = Integer.parseInt(access.getProperty(Config.AAF_USER_EXPIRES, Config.AAF_USER_EXPIRES_DEF).trim())-1; // zero based
182
183             app=FQI.reverseDomain(si.defSS.getID());
184             realm = access.getProperty(Config.AAF_DEFAULT_REALM, Config.getDefaultRealm());
185
186             env = new RosettaEnv();
187             permsDF = env.newDataFactory(Perms.class);
188             usersDF = env.newDataFactory(Users.class);
189             certsDF = env.newDataFactory(Certs.class);
190             certsDF.rootMarshal(new CertsMarshal()); // Speedier Marshaling
191             credReqDF = env.newDataFactory(CredRequest.class);
192             errDF = env.newDataFactory(Error.class);
193         } catch (APIException e) {
194             throw new CadiException("AAFCon cannot be configured",e);
195         }
196     }
197     protected abstract URI initURI();
198     protected abstract void setInitURI(String uriString) throws CadiException;
199
200     public final String aafVersion() {
201         return apiVersion;
202     }
203
204     /**
205      * Use this call to get the appropriate client based on configuration (HTTP, future)
206      * using default AAF API Version
207      *
208      * @param apiVersion
209      * @return
210      * @throws CadiException
211      */
212     public Rcli<CLIENT> client() throws CadiException {
213         return client(apiVersion);
214     }
215
216     /**
217      * Use this call to get the appropriate client based on configuration (HTTP, future)
218      *
219      * @param apiVersion
220      * @return
221      * @throws CadiException
222      */
223     public Rcli<CLIENT> client(final String apiVersion) throws CadiException {
224         Rcli<CLIENT> client = clients.get(apiVersion);
225         if (client==null) {
226             client = rclient(initURI(),si.defSS);
227             client.apiVersion(apiVersion)
228                   .readTimeout(connTimeout);
229             clients.put(apiVersion, client);
230         }
231         return client;
232     }
233
234     public Rcli<CLIENT> client(URI uri) throws CadiException {
235         return rclient(uri,si.defSS).readTimeout(connTimeout);
236     }
237
238     /**
239      * Use this API when you have permission to have your call act as the end client's ID.
240      *
241      *  Your calls will get 403 errors if you do not have this permission.  it is a special setup, rarely given.
242      *
243      * @param apiVersion
244      * @param req
245      * @return
246      * @throws CadiException
247      */
248     public Rcli<CLIENT> clientAs(TaggedPrincipal p) throws CadiException {
249        return clientAs(apiVersion,p);
250     }
251
252     /**
253      * Use this API when you have permission to have your call act as the end client's ID.
254      *
255      *  Your calls will get 403 errors if you do not have this permission.  it is a special setup, rarely given.
256      *
257      * @param apiVersion
258      * @param req
259      * @return
260      * @throws CadiException
261      */
262     public Rcli<CLIENT> clientAs(String apiVersion, TaggedPrincipal p) throws CadiException {
263         Rcli<CLIENT> cl = client(apiVersion);
264         return cl.forUser(transferSS(p));
265     }
266
267
268     public RosettaEnv env() {
269         return env;
270     }
271
272     /**
273      * Return the backing AAFCon, if there is a Lur Setup that is AAF.
274      *
275      * If there is no AAFLur setup, it will return "null"
276      * @param servletRequest
277      * @return
278      */
279     public static final AAFCon<?> obtain(Object servletRequest) {
280         if (servletRequest instanceof CadiWrap) {
281             Lur lur = ((CadiWrap)servletRequest).getLur();
282             if (lur != null) {
283                 if (lur instanceof EpiLur) {
284                     AbsAAFLur<?> aal = (AbsAAFLur<?>) ((EpiLur)lur).subLur(AbsAAFLur.class);
285                     if (aal!=null) {
286                         return aal.aaf;
287                     }
288                 } else {
289                     if (lur instanceof AbsAAFLur) {
290                         return ((AbsAAFLur<?>)lur).aaf;
291                     }
292                 }
293             }
294         }
295         return null;
296     }
297
298     public abstract AAFCon<CLIENT> clone(String url) throws CadiException, LocatorException;
299
300     public AAFAuthn<CLIENT> newAuthn() throws APIException {
301         try {
302             return new AAFAuthn<>(this);
303         } catch (Exception e) {
304             throw new APIException(e);
305         }
306     }
307
308     public AAFAuthn<CLIENT> newAuthn(AbsUserCache<AAFPermission> c) {
309         return new AAFAuthn<>(this, c);
310     }
311
312     public AAFLurPerm newLur() throws CadiException {
313         try {
314             if (lur==null) {
315                 lur = new AAFLurPerm(this);
316                 return lur;
317             } else {
318                 return new AAFLurPerm(this,lur);
319             }
320         } catch (CadiException e) {
321             throw e;
322         } catch (Exception e) {
323             throw new CadiException(e);
324         }
325     }
326
327     public AAFLurPerm newLur(AbsUserCache<AAFPermission> c) throws APIException {
328         try {
329             return new AAFLurPerm(this,c);
330         } catch (APIException e) {
331             throw e;
332         } catch (Exception e) {
333             throw new APIException(e);
334         }
335     }
336
337     protected abstract Rcli<CLIENT> rclient(URI uri, SecuritySetter<CLIENT> ss) throws CadiException;
338
339     public abstract Rcli<CLIENT> rclient(Locator<URI> loc, SecuritySetter<CLIENT> ss) throws CadiException;
340
341     public Rcli<CLIENT> client(Locator<URI> locator) throws CadiException {
342         return rclient(locator,si.defSS);
343     }
344
345     public abstract<RET> RET best(Retryable<RET> retryable) throws LocatorException, CadiException, APIException;
346
347     public abstract<RET> RET bestForUser(GetSetter get, Retryable<RET> retryable) throws LocatorException, CadiException, APIException;
348
349     public abstract SecuritySetter<CLIENT> basicAuth(String user, String password) throws CadiException;
350
351     public abstract SecuritySetter<CLIENT> transferSS(TaggedPrincipal principal) throws CadiException;
352
353     public abstract SecuritySetter<CLIENT> basicAuthSS(BasicPrincipal principal) throws CadiException;
354
355     public abstract SecuritySetter<CLIENT> tokenSS(final String client_id, final String accessToken) throws CadiException;
356
357     public abstract SecuritySetter<CLIENT> x509Alias(String alias) throws APIException, CadiException;
358
359
360     public String getRealm() {
361         return realm;
362
363     }
364
365     /**
366      * This interface allows the AAFCon, even though generic, to pass in correctly typed values based on the above SS commands.
367      * @author Jonathan
368      *
369      */
370     public interface GetSetter {
371         public<CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException;
372     }
373
374     public SecuritySetter<CLIENT> set(final SecuritySetter<CLIENT> ss) {
375         si.set(ss);
376         for (Rcli<CLIENT> client : clients.values()) {
377             client.setSecuritySetter(ss);
378         }
379         return ss;
380     }
381
382     public SecurityInfoC<CLIENT> securityInfo() {
383         return si;
384     }
385
386     public String defID() {
387         if (si!=null) {
388             return si.defSS.getID();
389         }
390         return "unknown";
391     }
392
393     public void invalidate() throws CadiException {
394         for (Rcli<CLIENT> client : clients.values()) {
395             client.invalidate();
396         }
397         clients.clear();
398     }
399
400     public String readableErrMsg(Future<?> f) {
401         String text = f.body();
402         if (text==null || text.length()==0) {
403             text = f.code() + ": **No Message**";
404         } else if (text.contains("%")) {
405             try {
406                 Error err = errDF.newData().in(TYPE.JSON).load(f.body()).asObject();
407                 return Vars.convert(err.getText(),err.getVariables());
408             } catch (APIException e){
409                 access.log(e);
410             }
411         }
412         return text;
413     }
414
415     public static AAFCon<?> newInstance(PropAccess pa) throws CadiException, LocatorException {
416         // Potentially add plugin for other kinds of Access
417         return new AAFConHttp(pa);
418     }
419 }