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