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