047663c303a717a3c57d2dfcafdf051fe2405953
[aaf/authz.git] / auth / auth-locate / src / main / java / org / onap / aaf / auth / locate / facade / LocateFacadeImpl.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.auth.locate.facade;
23
24
25 import static org.onap.aaf.auth.layer.Result.ERR_ActionNotCompleted;
26 import static org.onap.aaf.auth.layer.Result.ERR_BadData;
27 import static org.onap.aaf.auth.layer.Result.ERR_ConflictAlreadyExists;
28 import static org.onap.aaf.auth.layer.Result.ERR_Denied;
29 import static org.onap.aaf.auth.layer.Result.ERR_NotFound;
30 import static org.onap.aaf.auth.layer.Result.ERR_NotImplemented;
31 import static org.onap.aaf.auth.layer.Result.ERR_Policy;
32 import static org.onap.aaf.auth.layer.Result.ERR_Security;
33 import static org.onap.aaf.auth.layer.Result.OK;
34
35 import java.lang.reflect.Method;
36 import java.util.HashMap;
37 import java.util.Map;
38
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
41
42 import org.onap.aaf.auth.dao.cass.Status;
43 import org.onap.aaf.auth.env.AuthzEnv;
44 import org.onap.aaf.auth.env.AuthzTrans;
45 import org.onap.aaf.auth.layer.FacadeImpl;
46 import org.onap.aaf.auth.layer.Result;
47 import org.onap.aaf.auth.locate.mapper.Mapper;
48 import org.onap.aaf.auth.locate.mapper.Mapper.API;
49 import org.onap.aaf.auth.locate.service.LocateService;
50 import org.onap.aaf.auth.locate.service.LocateServiceImpl;
51 import org.onap.aaf.auth.rserv.RServlet;
52 import org.onap.aaf.auth.rserv.RouteReport;
53 import org.onap.aaf.auth.rserv.doc.ApiDoc;
54 import org.onap.aaf.cadi.aaf.client.Examples;
55 import org.onap.aaf.misc.env.APIException;
56 import org.onap.aaf.misc.env.Data;
57 import org.onap.aaf.misc.env.Data.TYPE;
58 import org.onap.aaf.misc.env.Env;
59 import org.onap.aaf.misc.env.TimeTaken;
60 import org.onap.aaf.misc.rosetta.env.RosettaDF;
61 import org.onap.aaf.misc.rosetta.env.RosettaData;
62 import org.owasp.encoder.Encode;
63
64 import locate_local.v1_0.Api;
65
66
67 /**
68  * AuthzFacade
69  *
70  * This Service Facade encapsulates the essence of the API Service can do, and provides
71  * a single created object for elements such as RosettaDF.
72  *
73  * The Responsibilities of this class are to:
74  * 1) Interact with the Service Implementation (which might be supported by various kinds of Backend Storage)
75  * 2) Validate incoming data (if applicable)
76  * 3) Convert the Service response into the right Format, and mark the Content Type
77  *         a) In the future, we may support multiple Response Formats, aka JSON or XML, based on User Request.
78  * 4) Log Service info, warnings and exceptions as necessary
79  * 5) When asked by the API layer, this will create and write Error content to the OutputStream
80  *
81  * Note: This Class does NOT set the HTTP Status Code.  That is up to the API layer, so that it can be
82  * clearly coordinated with the API Documentation
83  *
84  * @author Jonathan
85  *
86  */
87 public abstract class LocateFacadeImpl<IN,OUT,ENDPOINTS,MGMT_ENDPOINTS,CONFIGURATION,ERROR> extends FacadeImpl implements LocateFacade
88     {
89     private LocateService<IN,OUT,ENDPOINTS,MGMT_ENDPOINTS,CONFIGURATION,ERROR> service;
90
91     private final RosettaDF<ERROR>             errDF;
92     private final RosettaDF<Api>                 apiDF;
93     private final RosettaDF<ENDPOINTS>        epDF;
94     private final RosettaDF<MGMT_ENDPOINTS>    mepDF;
95     private final RosettaDF<CONFIGURATION>    confDF;
96
97
98     private static long cacheClear = 0L, emptyCheck=0L;
99     private final static Map<String,String> epsCache = new HashMap<>(); // protected manually, in getEndpoints
100
101     public LocateFacadeImpl(AuthzEnv env, LocateService<IN,OUT,ENDPOINTS,MGMT_ENDPOINTS,CONFIGURATION,ERROR> service, Data.TYPE dataType) throws APIException {
102         this.service = service;
103         (errDF                 = env.newDataFactory(mapper().getClass(API.ERROR))).in(dataType).out(dataType);
104         (apiDF                = env.newDataFactory(Api.class)).in(dataType).out(dataType);
105         (epDF                = env.newDataFactory(mapper().getClass(API.ENDPOINTS))).in(dataType).out(dataType);
106         (mepDF                = env.newDataFactory(mapper().getClass(API.MGMT_ENDPOINTS))).in(dataType).out(dataType);
107         (confDF                = env.newDataFactory(mapper().getClass(API.CONFIG))).in(dataType).out(dataType);
108     }
109
110     public Mapper<IN,OUT,ENDPOINTS,MGMT_ENDPOINTS,CONFIGURATION,ERROR> mapper() {
111         return service.mapper();
112     }
113
114     /* (non-Javadoc)
115      * @see com.att.authz.facade.AuthzFacade#error(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, int)
116      *
117      * Note: Conforms to AT&T TSS RESTful Error Structure
118      */
119     @Override
120     public void error(AuthzTrans trans, HttpServletResponse response, Result<?> result) {
121         String msg = result.details==null?"":result.details.trim();
122         String[] detail;
123         if (result.variables==null) {
124             detail = new String[1];
125         } else {
126             int l = result.variables.length;
127             detail=new String[l+1];
128             System.arraycopy(result.variables, 0, detail, 1, l);
129         }
130         error(trans, response, result.status,msg,detail);
131     }
132
133     @Override
134     public void error(AuthzTrans trans, HttpServletResponse response, int status, String msg, String ... _detail) {
135             String[] detail = _detail;
136         if (detail.length==0) {
137             detail=new String[1];
138         }
139         boolean hidemsg = false;
140         String msgId;
141         switch(status) {
142             case 202:
143             case ERR_ActionNotCompleted:
144                 msgId = "SVC1202";
145                 detail[0] = "Accepted, Action not complete";
146                 response.setStatus(/*httpstatus=*/202);
147                 break;
148
149             case 403:
150             case ERR_Policy:
151             case ERR_Security:
152             case ERR_Denied:
153                 msgId = "SVC1403";
154                 detail[0] = "Forbidden";
155                 response.setStatus(/*httpstatus=*/403);
156                 break;
157
158             case 404:
159             case ERR_NotFound:
160                 msgId = "SVC1404";
161                 detail[0] = "Not Found";
162                 response.setStatus(/*httpstatus=*/404);
163                 break;
164
165             case 406:
166             case ERR_BadData:
167                 msgId="SVC1406";
168                 detail[0] = "Not Acceptable";
169                 response.setStatus(/*httpstatus=*/406);
170                 break;
171
172             case 409:
173             case ERR_ConflictAlreadyExists:
174                 msgId = "SVC1409";
175                 detail[0] = "Conflict Already Exists";
176                 response.setStatus(/*httpstatus=*/409);
177                 break;
178
179             case 501:
180             case ERR_NotImplemented:
181                 msgId = "SVC1501";
182                 detail[0] = "Not Implemented";
183                 response.setStatus(/*httpstatus=*/501);
184                 break;
185
186             default:
187                 msgId = "SVC1500";
188                 detail[0] = "General Service Error";
189                 response.setStatus(/*httpstatus=*/500);
190                 hidemsg = true;
191                 break;
192         }
193
194         try {
195             StringBuilder holder = new StringBuilder();
196             ERROR em = mapper().errorFromMessage(holder,msgId,msg,detail);
197             trans.checkpoint(
198                     "ErrResp [" +
199                     msgId +
200                     "] " +
201                     holder.toString(),
202                     Env.ALWAYS);
203             if (hidemsg) {
204                 holder.setLength(0);
205                 em = mapper().errorFromMessage(holder, msgId, "Server had an issue processing this request");
206             }
207             errDF.newData(trans).load(em).to(response.getOutputStream());
208
209         } catch (Exception e) {
210             trans.error().log(e,"unable to send response for",msg);
211         }
212     }
213
214     /* (non-Javadoc)
215      * @see com.att.authz.facade.AuthzFacade#getAPI(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse)
216      */
217     public final static String API_REPORT = "apiReport";
218     @Override
219     public Result<Void> getAPI(AuthzTrans trans, HttpServletResponse resp, RServlet<AuthzTrans> rservlet) {
220         TimeTaken tt = trans.start(API_REPORT, Env.SUB);
221         try {
222             Api api = new Api();
223             Api.Route ar;
224             Method[] meths = LocateServiceImpl.class.getDeclaredMethods();
225             for (RouteReport rr : rservlet.routeReport()) {
226                 api.getRoute().add(ar = new Api.Route());
227                 ar.setMeth(rr.meth.name());
228                 ar.setPath(rr.path);
229                 ar.setDesc(rr.desc);
230                 ar.getContentType().addAll(rr.contextTypes);
231                 for (Method m : meths) {
232                     ApiDoc ad;
233                     if ((ad = m.getAnnotation(ApiDoc.class))!=null &&
234                             rr.meth.equals(ad.method()) &&
235                             rr.path.equals(ad.path())) {
236                         for (String param : ad.params()) {
237                             ar.getParam().add(param);
238                         }
239                         for (String text : ad.text()) {
240                             ar.getComments().add(text);
241                         }
242                         ar.setExpected(ad.expectedCode());
243                         for (int ec : ad.errorCodes()) {
244                             ar.getExplicitErr().add(ec);
245                         }
246                     }
247                 }
248             }
249             apiDF.newData(trans).load(api).to(resp.getOutputStream());
250             setContentType(resp,apiDF.getOutType());
251             return Result.ok();
252
253         } catch (Exception e) {
254             trans.error().log(e,IN,API_REPORT);
255             return Result.err(e);
256         } finally {
257             tt.done();
258         }
259     }
260
261     public final static String API_EXAMPLE = "apiExample";
262     /* (non-Javadoc)
263      * @see com.att.authz.facade.AuthzFacade#getAPIExample(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, java.lang.String)
264      */
265     @Override
266     public Result<Void> getAPIExample(AuthzTrans trans, HttpServletResponse resp, String nameOrContentType, boolean optional) {
267         TimeTaken tt = trans.start(API_EXAMPLE, Env.SUB);
268         try {
269             String content =Examples.print(apiDF.getEnv(), nameOrContentType, optional);
270             resp.getOutputStream().print(Encode.forJava(content));
271             setContentType(resp,content.contains("<?xml")?TYPE.XML:TYPE.JSON);
272             return Result.ok();
273         } catch (Exception e) {
274             trans.error().log(e,IN,API_EXAMPLE);
275             return Result.err(Result.ERR_NotImplemented,e.getMessage());
276         } finally {
277             tt.done();
278         }
279     }
280
281     public final static String GET_ENDPOINTS = "getEndpoints";
282     private final static Object LOCK = new Object();
283     /* (non-Javadoc)
284      * @see org.onap.aaf.auth.locate.facade.GwFacade#getEndpoints(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletResponse, java.lang.String, java.lang.String, java.lang.String)
285      */
286     @Override
287     public Result<Void> getEndpoints(AuthzTrans trans, HttpServletResponse resp, String key, String service, String version, String other) {
288         TimeTaken tt = trans.start(GET_ENDPOINTS, Env.SUB);
289         try {
290             String output=null;
291             long temp=System.currentTimeMillis();
292             synchronized(LOCK) {
293                 if (cacheClear<temp) {
294                     epsCache.clear();
295                     cacheClear = temp+1000*60*2; // 2 mins standard cache clear
296                 } else {
297                     output = epsCache.get(key);
298                     if ("{}".equals(output) && emptyCheck<temp) {
299                         output = null;
300                         emptyCheck = temp+5000; // 5 second check
301                     }
302                 }
303             }
304             if (output==null) {
305                 Result<ENDPOINTS> reps = this.service.getEndPoints(trans,service,version,other);
306                 if (reps.notOK()) {
307                     return Result.err(reps);
308                 } else {
309                     output = epDF.newData(trans).load(reps.value).asString();
310                     synchronized(LOCK) {
311                         epsCache.put(key, output);
312                     }
313                 }
314             }
315             resp.getOutputStream().println(Encode.forJava(output));
316             setContentType(resp,epDF.getOutType());
317             return Result.ok();
318         } catch (Exception e) {
319             trans.error().log(e,IN,API_EXAMPLE);
320             return Result.err(Result.ERR_NotImplemented,e.getMessage());
321         } finally {
322             tt.done();
323         }
324     }
325
326     private static final String PUT_MGMT_ENDPOINTS = "Put Mgmt Endpoints";
327     /* (non-Javadoc)
328      * @see org.onap.aaf.auth.locate.facade.GwFacade#putMgmtEndpoints(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
329      */
330     @Override
331     public Result<Void> putMgmtEndpoints(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
332         TimeTaken tt = trans.start(PUT_MGMT_ENDPOINTS, Env.SUB|Env.ALWAYS);
333         try {
334             MGMT_ENDPOINTS rreq;
335             try {
336                 RosettaData<MGMT_ENDPOINTS> data = mepDF.newData().load(req.getInputStream());
337                 rreq = data.asObject();
338             } catch (APIException e) {
339                 trans.error().log("Invalid Input",IN,PUT_MGMT_ENDPOINTS);
340                 return Result.err(Status.ERR_BadData,"Invalid Input");
341
342             }
343             Result<Void> rp = service.putMgmtEndPoints(trans, rreq);
344             switch(rp.status) {
345                 case OK:
346                     synchronized(LOCK) {
347                         cacheClear = 0L;
348                     }
349                     setContentType(resp,mepDF.getOutType());
350                     return Result.ok();
351                 default:
352                     return rp;
353             }
354         } catch (Exception e) {
355             trans.error().log(e,IN,PUT_MGMT_ENDPOINTS);
356             return Result.err(e);
357         } finally {
358             tt.done();
359         }
360     }
361
362     private static final String DELETE_MGMT_ENDPOINTS = "Delete Mgmt Endpoints";
363     /* (non-Javadoc)
364      * @see org.onap.aaf.auth.locate.facade.GwFacade#removeMgmtEndpoints(org.onap.aaf.auth.env.test.AuthzTrans, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
365      */
366     @Override
367     public Result<Void> removeMgmtEndpoints(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp) {
368         TimeTaken tt = trans.start(DELETE_MGMT_ENDPOINTS, Env.SUB|Env.ALWAYS);
369         try {
370             MGMT_ENDPOINTS rreq;
371             try {
372                 RosettaData<MGMT_ENDPOINTS> data = mepDF.newData().load(req.getInputStream());
373                 rreq = data.asObject();
374             } catch (APIException e) {
375                 trans.error().log("Invalid Input",IN,DELETE_MGMT_ENDPOINTS);
376                 return Result.err(Status.ERR_BadData,"Invalid Input");
377
378             }
379             Result<Void> rp = service.removeMgmtEndPoints(trans, rreq);
380             switch(rp.status) {
381                 case OK:
382                     synchronized(LOCK) {
383                         cacheClear = 0L;
384                     }
385                     setContentType(resp,mepDF.getOutType());
386                     return Result.ok();
387                 default:
388                     return rp;
389             }
390         } catch (Exception e) {
391             trans.error().log(e,IN,DELETE_MGMT_ENDPOINTS);
392             return Result.err(e);
393         } finally {
394             tt.done();
395         }
396     }
397
398     private static final String GET_CONFIG = "Get Configuration";
399     @Override
400     public Result<Void> getConfig(AuthzTrans trans, HttpServletRequest req, HttpServletResponse resp, final String id, final String type) {
401         TimeTaken tt = trans.start(GET_CONFIG, Env.SUB|Env.ALWAYS);
402         try {
403             Result<CONFIGURATION> rp = service.getConfig(trans, id, type);
404             switch(rp.status) {
405                 case OK:
406                     setContentType(resp,mepDF.getOutType());
407                     confDF.newData(trans).load(rp.value).to(resp.getOutputStream());
408                     return Result.ok();
409                 default:
410                     return Result.err(rp);
411             }
412         } catch (Exception e) {
413             trans.error().log(e,IN,GET_CONFIG);
414             return Result.err(e);
415         } finally {
416             tt.done();
417         }
418     }
419
420 }