Improved multi Proxy DNSLocator based
[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             //TODO Get Realm from AAF
185             realm="people.osaaf.org";
186     
187             env = new RosettaEnv();
188             permsDF = env.newDataFactory(Perms.class);
189             usersDF = env.newDataFactory(Users.class);
190             certsDF = env.newDataFactory(Certs.class);
191             certsDF.rootMarshal(new CertsMarshal()); // Speedier Marshaling
192             credReqDF = env.newDataFactory(CredRequest.class);
193             errDF = env.newDataFactory(Error.class);
194         } catch (APIException e) {
195             throw new CadiException("AAFCon cannot be configured",e);
196         }
197     }
198     protected abstract URI initURI();
199     protected abstract void setInitURI(String uriString) throws CadiException;
200
201     public final String aafVersion() {
202         return apiVersion;
203     }
204     
205     /**
206      * Use this call to get the appropriate client based on configuration (HTTP, future)
207      * using default AAF API Version
208      * 
209      * @param apiVersion
210      * @return
211      * @throws CadiException
212      */
213     public Rcli<CLIENT> client() throws CadiException {
214         return client(apiVersion);
215     }        
216
217     /**
218      * Use this call to get the appropriate client based on configuration (HTTP, future)
219      * 
220      * @param apiVersion
221      * @return
222      * @throws CadiException
223      */
224     public Rcli<CLIENT> client(final String apiVersion) throws CadiException {
225         Rcli<CLIENT> client = clients.get(apiVersion);
226         if (client==null) {
227             client = rclient(initURI(),si.defSS);
228             client.apiVersion(apiVersion)
229                   .readTimeout(connTimeout);
230             clients.put(apiVersion, client);
231         } 
232         return client;
233     }
234
235     public Rcli<CLIENT> client(URI uri) throws CadiException {
236         return rclient(uri,si.defSS).readTimeout(connTimeout);
237     }
238     
239     /**
240      * Use this API when you have permission to have your call act as the end client's ID.
241      * 
242      *  Your calls will get 403 errors if you do not have this permission.  it is a special setup, rarely given.
243      * 
244      * @param apiVersion
245      * @param req
246      * @return
247      * @throws CadiException
248      */
249     public Rcli<CLIENT> clientAs(TaggedPrincipal p) throws CadiException {
250        return clientAs(apiVersion,p);
251     }
252     
253     /**
254      * Use this API when you have permission to have your call act as the end client's ID.
255      * 
256      *  Your calls will get 403 errors if you do not have this permission.  it is a special setup, rarely given.
257      * 
258      * @param apiVersion
259      * @param req
260      * @return
261      * @throws CadiException
262      */
263     public Rcli<CLIENT> clientAs(String apiVersion, TaggedPrincipal p) throws CadiException {
264         Rcli<CLIENT> cl = client(apiVersion);
265         return cl.forUser(transferSS(p));
266     }
267
268     
269     public RosettaEnv env() {
270         return env;
271     }
272     
273     /**
274      * Return the backing AAFCon, if there is a Lur Setup that is AAF.
275      * 
276      * If there is no AAFLur setup, it will return "null"
277      * @param servletRequest
278      * @return
279      */
280     public static final AAFCon<?> obtain(Object servletRequest) {
281         if (servletRequest instanceof CadiWrap) {
282             Lur lur = ((CadiWrap)servletRequest).getLur();
283             if (lur != null) {
284                 if (lur instanceof EpiLur) {
285                     AbsAAFLur<?> aal = (AbsAAFLur<?>) ((EpiLur)lur).subLur(AbsAAFLur.class);
286                     if (aal!=null) {
287                         return aal.aaf;
288                     }
289                 } else {
290                     if (lur instanceof AbsAAFLur) {
291                         return ((AbsAAFLur<?>)lur).aaf;
292                     }
293                 }
294             }
295         }
296         return null;
297     }
298     
299     public abstract AAFCon<CLIENT> clone(String url) throws CadiException, LocatorException;
300     
301     public AAFAuthn<CLIENT> newAuthn() throws APIException {
302         try {
303             return new AAFAuthn<>(this);
304         } catch (Exception e) {
305             throw new APIException(e);
306         }
307     }
308
309     public AAFAuthn<CLIENT> newAuthn(AbsUserCache<AAFPermission> c) {
310         return new AAFAuthn<>(this, c);
311     }
312
313     public AAFLurPerm newLur() throws CadiException {
314         try {
315             if (lur==null) {
316                 lur = new AAFLurPerm(this);
317                 return lur;
318             } else {
319                 return new AAFLurPerm(this,lur);
320             }
321         } catch (CadiException e) {
322             throw e;
323         } catch (Exception e) {
324             throw new CadiException(e);
325         }
326     }
327     
328     public AAFLurPerm newLur(AbsUserCache<AAFPermission> c) throws APIException {
329         try {
330             return new AAFLurPerm(this,c);
331         } catch (APIException e) {
332             throw e;
333         } catch (Exception e) {
334             throw new APIException(e);
335         }
336     }
337
338     protected abstract Rcli<CLIENT> rclient(URI uri, SecuritySetter<CLIENT> ss) throws CadiException;
339     
340     public abstract Rcli<CLIENT> rclient(Locator<URI> loc, SecuritySetter<CLIENT> ss) throws CadiException;
341
342     public Rcli<CLIENT> client(Locator<URI> locator) throws CadiException {
343         return rclient(locator,si.defSS);
344     }
345     
346     public abstract<RET> RET best(Retryable<RET> retryable) throws LocatorException, CadiException, APIException;
347
348     public abstract<RET> RET bestForUser(GetSetter get, Retryable<RET> retryable) throws LocatorException, CadiException, APIException;
349
350     public abstract SecuritySetter<CLIENT> basicAuth(String user, String password) throws CadiException;
351     
352     public abstract SecuritySetter<CLIENT> transferSS(TaggedPrincipal principal) throws CadiException;
353     
354     public abstract SecuritySetter<CLIENT> basicAuthSS(BasicPrincipal principal) throws CadiException;
355     
356     public abstract SecuritySetter<CLIENT> tokenSS(final String client_id, final String accessToken) throws CadiException;
357     
358     public abstract SecuritySetter<CLIENT> x509Alias(String alias) throws APIException, CadiException;
359     
360
361     public String getRealm() {
362         return realm;
363
364     }
365     
366     /**
367      * This interface allows the AAFCon, even though generic, to pass in correctly typed values based on the above SS commands.
368      * @author Jonathan
369      *
370      */
371     public interface GetSetter {
372         public<CLIENT> SecuritySetter<CLIENT> get(AAFCon<CLIENT> con) throws CadiException;
373     }
374
375     public SecuritySetter<CLIENT> set(final SecuritySetter<CLIENT> ss) {
376         si.set(ss);
377         for (Rcli<CLIENT> client : clients.values()) {
378             client.setSecuritySetter(ss);
379         }
380         return ss;
381     }
382     
383     public SecurityInfoC<CLIENT> securityInfo() {
384         return si;
385     }
386
387     public String defID() {
388         if (si!=null) {
389             return si.defSS.getID();
390         }
391         return "unknown";
392     }
393     
394     public void invalidate() throws CadiException {
395         for (Rcli<CLIENT> client : clients.values()) {
396             client.invalidate();
397         }
398         clients.clear();
399     }
400
401     public String readableErrMsg(Future<?> f) {
402         String text = f.body();
403         if (text==null || text.length()==0) {
404             text = f.code() + ": **No Message**";
405         } else if (text.contains("%")) {
406             try {
407                 Error err = errDF.newData().in(TYPE.JSON).load(f.body()).asObject();
408                 return Vars.convert(err.getText(),err.getVariables());
409             } catch (APIException e){
410                 access.log(e);
411             }
412         }
413         return text;
414     }
415     
416     public static AAFCon<?> newInstance(PropAccess pa) throws CadiException, LocatorException {
417         // Potentially add plugin for other kinds of Access
418         return new AAFConHttp(pa);
419     }
420 }