Sonar Fixes to increate Coverage on Unit tests
[music.git] / src / main / java / org / onap / music / main / CachingUtil.java
1 /*
2  * ============LICENSE_START==========================================
3  * org.onap.music
4  * ===================================================================
5  *  Copyright (c) 2017 AT&T Intellectual Property
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  * 
19  * ============LICENSE_END=============================================
20  * ====================================================================
21  */
22 package org.onap.music.main;
23
24 import java.util.Arrays;
25 import java.util.Calendar;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.UUID;
30 import javax.ws.rs.core.HttpHeaders;
31 import javax.ws.rs.core.MediaType;
32 import org.apache.commons.codec.binary.Base64;
33 import org.apache.commons.jcs.JCS;
34 import org.apache.commons.jcs.access.CacheAccess;
35 import org.codehaus.jackson.map.ObjectMapper;
36 import org.onap.music.datastore.PreparedQueryObject;
37 import org.onap.music.datastore.jsonobjects.AAFResponse;
38 import org.onap.music.eelf.logging.EELFLoggerDelegate;
39 import org.onap.music.exceptions.MusicServiceException;
40
41 import com.att.eelf.configuration.EELFLogger;
42 import com.datastax.driver.core.DataType;
43 import com.datastax.driver.core.ResultSet;
44 import com.datastax.driver.core.Row;
45 import com.sun.jersey.api.client.Client;
46 import com.sun.jersey.api.client.ClientResponse;
47 import com.sun.jersey.api.client.WebResource;
48
49 /**
50  * All Caching related logic is handled by this class and a schedule cron runs to update cache.
51  * 
52  * @author Vikram
53  *
54  */
55 public class CachingUtil implements Runnable {
56
57     private static EELFLoggerDelegate logger = EELFLoggerDelegate.getLogger(CachingUtil.class);
58
59     private static CacheAccess<String, String> musicCache = JCS.getInstance("musicCache");
60     private static CacheAccess<String, String> musicLockCache = JCS.getInstance("musicLockCache");
61     private static CacheAccess<String, Map<String, String>> aafCache = JCS.getInstance("aafCache");
62     private static CacheAccess<String, String> appNameCache = JCS.getInstance("appNameCache");
63     private static Map<String, Number> userAttempts = new HashMap<>();
64     private static Map<String, Calendar> lastFailedTime = new HashMap<>();
65
66     public boolean isCacheRefreshNeeded() {
67         if (aafCache.get("initBlankMap") == null)
68             return true;
69         return false;
70     }
71
72     public void initializeMusicCache() {
73         logger.info(EELFLoggerDelegate.applicationLogger,"Initializing Music Cache...");
74         musicCache.put("isInitialized", "true");
75     }
76
77     public void initializeAafCache() throws MusicServiceException {
78         logger.info(EELFLoggerDelegate.applicationLogger,"Resetting and initializing AAF Cache...");
79
80         String query = "SELECT uuid, application_name, keyspace_name, username, password FROM admin.keyspace_master WHERE is_api = ? allow filtering";
81         PreparedQueryObject pQuery = new PreparedQueryObject();
82         pQuery.appendQueryString(query);
83         try {
84             pQuery.addValue(MusicUtil.convertToActualDataType(DataType.cboolean(), false));
85         } catch (Exception e1) {
86             e1.printStackTrace();
87             logger.error(EELFLoggerDelegate.errorLogger,"Exception is "+ e1.getMessage() + "during initalizeAafCache");
88         }
89         ResultSet rs = MusicCore.get(pQuery);
90         Iterator<Row> it = rs.iterator();
91         Map<String, String> map = null;
92         while (it.hasNext()) {
93             Row row = it.next();
94             String nameSpace = row.getString("keyspace_name");
95             String userId = row.getString("username");
96             String password = row.getString("password");
97             String keySpace = row.getString("application_name");
98             String uuid = row.getUUID("uuid").toString();
99             try {
100                 userAttempts.put(nameSpace, 0);
101                 AAFResponse responseObj = triggerAAF(nameSpace, userId, password);
102                 if (responseObj.getNs().size() > 0) {
103                     map = new HashMap<>();
104                     map.put(userId, password);
105                     aafCache.put(nameSpace, map);
106                     musicCache.put(nameSpace, keySpace);
107                     musicLockCache.put(nameSpace, uuid);
108                     logger.debug("Cronjob: Cache Updated with AAF response for namespace "
109                                     + nameSpace);
110                 }
111             } catch (Exception e) {
112                 // TODO Auto-generated catch block
113                 e.printStackTrace();
114                 logger.error("Something at AAF was changed for ns: " + nameSpace
115                                 + ". So not updating Cache for the namespace. ");
116                 logger.error("Exception is " + e.getMessage());
117             }
118         }
119
120     }
121
122     @Override
123     public void run() {
124         logger.debug("Scheduled task invoked. Refreshing Cache...");
125         try {
126                         initializeAafCache();
127                 } catch (MusicServiceException e) {
128                         logger.error(EELFLoggerDelegate.errorLogger,e.getMessage());
129                 }
130     }
131
132     public static boolean authenticateAAFUser(String nameSpace, String userId, String password,
133                     String keySpace) throws Exception {
134
135         if (aafCache.get(nameSpace) != null) {
136             if (keySpace != null && !musicCache.get(nameSpace).equals(keySpace)) {
137                 logger.debug("Create new application for the same namespace.");
138             } else if (aafCache.get(nameSpace).get(userId).equals(password)) {
139                 logger.debug("Authenticated with cache value..");
140                 // reset invalid attempts to 0
141                 userAttempts.put(nameSpace, 0);
142                 return true;
143             } else {
144                 // call AAF update cache with new password
145                 if (userAttempts.get(nameSpace) == null)
146                     userAttempts.put(nameSpace, 0);
147                 if ((Integer) userAttempts.get(nameSpace) >= 3) {
148                     logger.info(EELFLoggerDelegate.applicationLogger,"Reached max attempts. Checking if time out..");
149                     logger.info(EELFLoggerDelegate.applicationLogger,"Failed time: "+lastFailedTime.get(nameSpace).getTime());
150                     Calendar calendar = Calendar.getInstance();
151                     long delayTime = (calendar.getTimeInMillis()-lastFailedTime.get(nameSpace).getTimeInMillis());
152                     logger.info(EELFLoggerDelegate.applicationLogger,"Delayed time: "+delayTime);
153                     if( delayTime > 120000) {
154                         logger.info(EELFLoggerDelegate.applicationLogger,"Resetting failed attempt.");
155                         userAttempts.put(nameSpace, 0);
156                     } else {
157                         throw new Exception("No more attempts allowed. Please wait for atleast 2 min.");
158                     }
159                 }
160                 logger.error(EELFLoggerDelegate.errorLogger,"Cache not authenticated..");
161                 logger.info(EELFLoggerDelegate.applicationLogger,"Check AAF again...");
162             }
163         }
164
165         AAFResponse responseObj = triggerAAF(nameSpace, userId, password);
166         if (responseObj.getNs().size() > 0) {
167             if (responseObj.getNs().get(0).getAdmin().contains(userId)) {
168                 //Map<String, String> map = new HashMap<>();
169                 //map.put(userId, password);
170                 //aafCache.put(nameSpace, map);
171                 return true;
172             }
173         }
174         logger.info(EELFLoggerDelegate.applicationLogger,"Invalid user. Cache not updated");
175         return false;
176     }
177
178     private static AAFResponse triggerAAF(String nameSpace, String userId, String password)
179                     throws Exception {
180         if (MusicUtil.getAafEndpointUrl() == null) {
181             throw new Exception("AAF endpoint is not set. Please specify in the properties file.");
182         }
183         Client client = Client.create();
184         // WebResource webResource =
185         // client.resource("https://aaftest.test.att.com:8095/proxy/authz/nss/"+nameSpace);
186         WebResource webResource = client.resource(MusicUtil.getAafEndpointUrl().concat(nameSpace));
187         String plainCreds = userId + ":" + password;
188         byte[] plainCredsBytes = plainCreds.getBytes();
189         byte[] base64CredsBytes = Base64.encodeBase64(plainCredsBytes);
190         String base64Creds = new String(base64CredsBytes);
191
192         ClientResponse response = webResource.accept(MediaType.APPLICATION_JSON)
193                         .header("Authorization", "Basic " + base64Creds)
194                         .header("content-type", "application/json").get(ClientResponse.class);
195         if (response.getStatus() != 200) {
196             if (userAttempts.get(nameSpace) == null)
197                 userAttempts.put(nameSpace, 0);
198             if ((Integer) userAttempts.get(nameSpace) >= 2) {
199                 lastFailedTime.put(nameSpace, Calendar.getInstance());
200                 userAttempts.put(nameSpace, ((Integer) userAttempts.get(nameSpace) + 1));
201                 throw new Exception(
202                                 "Reached max invalid attempts. Please contact admin and retry with valid credentials.");
203             }
204             userAttempts.put(nameSpace, ((Integer) userAttempts.get(nameSpace) + 1));
205             throw new Exception(
206                             "Unable to authenticate. Please check the AAF credentials against namespace.");
207             // TODO Allow for 2-3 times and forbid any attempt to trigger AAF with invalid values
208             // for specific time.
209         }
210         response.getHeaders().put(HttpHeaders.CONTENT_TYPE,
211                         Arrays.asList(MediaType.APPLICATION_JSON));
212         // AAFResponse output = response.getEntity(AAFResponse.class);
213         response.bufferEntity();
214         String x = response.getEntity(String.class);
215         AAFResponse responseObj = new ObjectMapper().readValue(x, AAFResponse.class);
216         return responseObj;
217     }
218
219     public static Map<String, Object> authenticateAIDUser(String aid, String keyspace)
220                     throws Exception {
221         Map<String, Object> resultMap = new HashMap<>();
222         String uuid = null;
223         /*
224          * if(aid == null || aid.length() == 0) { resultMap.put("Exception Message",
225          * "AID is missing for the keyspace requested."); //create a new AID ?? } else
226          */
227         if (musicCache.get(keyspace) == null) {
228             PreparedQueryObject pQuery = new PreparedQueryObject();
229             pQuery.appendQueryString(
230                             "SELECT uuid from admin.keyspace_master where keyspace_name = '"
231                                             + keyspace + "' allow filtering");
232             Row rs = MusicCore.get(pQuery).one();
233             try {
234                 uuid = rs.getUUID("uuid").toString();
235                 musicCache.put(keyspace, uuid);
236             } catch (Exception e) {
237                 String msg = e.getMessage();
238                 logger.error("Exception occured during uuid retrieval from DB." + e.getMessage());
239                 resultMap.put("Exception", "Unauthorized operation. Check AID and Keyspace. "
240                                 + "Exception from MUSIC is: "
241                                 + (msg == null ? "Keyspace is new so no AID should be passed in Header."
242                                                 : msg));
243                 return resultMap;
244             }
245             if (!musicCache.get(keyspace).toString().equals(aid)) {
246                 resultMap.put("Exception Message",
247                                 "Unauthorized operation. Invalid AID for the keyspace");
248                 return resultMap;
249             }
250         } else if (musicCache.get(keyspace) != null
251                         && !musicCache.get(keyspace).toString().equals(aid)) {
252             resultMap.put("Exception Message",
253                             "Unauthorized operation. Invalid AID for the keyspace");
254             return resultMap;
255         }
256         resultMap.put("aid", uuid);
257         return resultMap;
258     }
259     
260     
261     public static Map<String, Object> authenticateAIDUserLock(String aid, String nameSpace)
262             throws Exception {
263                 Map<String, Object> resultMap = new HashMap<>();
264                 String uuid = null;
265                 
266                 if (musicLockCache.get(nameSpace) == null) {
267                     PreparedQueryObject pQuery = new PreparedQueryObject();
268                     pQuery.appendQueryString(
269                                     "SELECT uuid from admin.keyspace_master where application_name = '"
270                                                     + nameSpace + "' allow filtering");
271                     Row rs = MusicCore.get(pQuery).one();
272                     try {
273                         uuid = rs.getUUID("uuid").toString();
274                         musicLockCache.put(nameSpace, uuid);
275                     } catch (Exception e) {
276                         logger.error("Exception occured during uuid retrieval from DB." + e.getMessage());
277                         resultMap.put("Exception", "Unauthorized operation. Check AID and Namespace. ");
278                         return resultMap;
279                     }
280                     if (!musicLockCache.get(nameSpace).toString().equals(aid)) {
281                         resultMap.put("Exception Message",
282                                         "Unauthorized operation. Invalid AID for the Namespace");
283                         return resultMap;
284                     }
285                 } else if (musicLockCache.get(nameSpace) != null
286                                 && !musicLockCache.get(nameSpace).toString().equals(aid)) {
287                     resultMap.put("Exception Message",
288                                     "Unauthorized operation. Invalid AID for the Namespace");
289                     return resultMap;
290                 }
291                 return resultMap;
292         }
293     
294     
295     
296
297     public static void updateMusicCache(String aid, String keyspace) {
298         logger.info("Updating musicCache for keyspace " + keyspace + " with aid " + aid);
299         musicCache.put(keyspace, aid);
300     }
301
302     public static void updateisAAFCache(String namespace, String isAAF) {
303         appNameCache.put(namespace, isAAF);
304     }
305
306     public static Boolean isAAFApplication(String namespace) throws MusicServiceException {
307
308         String isAAF = appNameCache.get(namespace);
309         if (isAAF == null) {
310             PreparedQueryObject pQuery = new PreparedQueryObject();
311             pQuery.appendQueryString(
312                             "SELECT is_aaf from admin.keyspace_master where application_name = '"
313                                             + namespace + "' allow filtering");
314             Row rs = MusicCore.get(pQuery).one();
315             try {
316                 isAAF = String.valueOf(rs.getBool("is_aaf"));
317                 appNameCache.put(namespace, isAAF);
318             } catch (Exception e) {
319                 logger.error("Exception occured during uuid retrieval from DB." + e.getMessage());
320                 e.printStackTrace();
321             }
322         }
323
324         
325         return Boolean.valueOf(isAAF);
326     }
327
328     public static String getUuidFromMusicCache(String keyspace) throws MusicServiceException {
329         String uuid = musicCache.get(keyspace);
330         if (uuid == null) {
331             PreparedQueryObject pQuery = new PreparedQueryObject();
332             pQuery.appendQueryString(
333                             "SELECT uuid from admin.keyspace_master where keyspace_name = '"
334                                             + keyspace + "' allow filtering");
335             Row rs = MusicCore.get(pQuery).one();
336             try {
337                 uuid = rs.getUUID("uuid").toString();
338                 musicCache.put(keyspace, uuid);
339             } catch (Exception e) {
340                 logger.error(EELFLoggerDelegate.errorLogger,"Exception occured during uuid retrieval from DB."+e.getMessage());
341                 e.printStackTrace();
342             }
343         }
344         return uuid;
345     }
346
347     public static String getAppName(String keyspace) throws MusicServiceException {
348         String appName = null;
349         PreparedQueryObject pQuery = new PreparedQueryObject();
350         pQuery.appendQueryString(
351                         "SELECT application_name from admin.keyspace_master where keyspace_name = '"
352                                         + keyspace + "' allow filtering");
353         Row rs = MusicCore.get(pQuery).one();
354         try {
355             appName = rs.getString("application_name");
356         } catch (Exception e) {
357             logger.error(EELFLoggerDelegate.errorLogger,"Exception occured during uuid retrieval from DB."+e.getMessage());
358             e.printStackTrace();
359         }
360         return appName;
361     }
362
363     public static String generateUUID() {
364         String uuid = UUID.randomUUID().toString();
365         logger.info(EELFLoggerDelegate.applicationLogger,"New AID generated: "+uuid);
366         return uuid;
367     }
368
369     public static Map<String, Object> validateRequest(String nameSpace, String userId,
370                     String password, String keyspace, String aid, String operation) {
371         Map<String, Object> resultMap = new HashMap<>();
372         if (!"createKeySpace".equals(operation)) {
373             if (nameSpace == null) {
374                 resultMap.put("Exception", "Application namespace is mandatory.");
375             }
376         }
377         return resultMap;
378
379     }
380
381     public static Map<String, Object> verifyOnboarding(String ns, String userId, String password) {
382         Map<String, Object> resultMap = new HashMap<>();
383         if (ns == null || userId == null || password == null) {
384             logger.error(EELFLoggerDelegate.errorLogger,"One or more required headers is missing. userId: "+userId+" :: password: "+password);
385             resultMap.put("Exception",
386                             "One or more required headers appName(ns), userId, password is missing. Please check.");
387             return resultMap;
388         }
389         PreparedQueryObject queryObject = new PreparedQueryObject();
390         queryObject.appendQueryString(
391                         "select * from admin.keyspace_master where application_name = ? and username = ? and password = ? allow filtering");
392         try {
393                 queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), ns));
394                 queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), userId));
395                 queryObject.addValue(MusicUtil.convertToActualDataType(DataType.text(), password));
396         } catch(Exception e) {
397                 resultMap.put("Exception",
398                     "Unable to process input data. Invalid input data type. Please check ns, userId and password values. "+e.getMessage());
399                 return resultMap;
400         }
401         Row rs = null;
402                 try {
403                         rs = MusicCore.get(queryObject).one();
404                 } catch (MusicServiceException e) {
405                         // TODO Auto-generated catch block
406                         e.printStackTrace();
407                         resultMap.put("Exception", "Unable to process operation. Error is "+e.getMessage());
408                         return resultMap;
409                 }
410         if (rs == null) {
411             logger.error(EELFLoggerDelegate.errorLogger,"Namespace, UserId and password doesn't match. namespace: "+ns+" and userId: "+userId);
412
413             resultMap.put("Exception", "Namespace, UserId and password doesn't match. namespace: "+ns+" and userId: "+userId);
414         } else {
415             boolean is_aaf = rs.getBool("is_aaf");
416             String keyspace = rs.getString("keyspace_name");
417             if (!is_aaf) {
418                 if (!keyspace.equals(MusicUtil.DEFAULTKEYSPACENAME)) {
419                     logger.error(EELFLoggerDelegate.errorLogger,"Non AAF applications are allowed to have only one keyspace per application.");
420                     resultMap.put("Exception",
421                                     "Non AAF applications are allowed to have only one keyspace per application.");
422                 }
423             }
424         }
425         return resultMap;
426     }
427 }