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