001/** 002 * Copyright 2005-2016 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.util; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.util.Truth; 020import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator; 021import org.kuali.rice.core.api.CoreApiServiceLocator; 022import org.kuali.rice.core.api.encryption.EncryptionService; 023import org.kuali.rice.core.api.util.type.KualiDecimal; 024import org.kuali.rice.coreservice.framework.parameter.ParameterConstants; 025import org.kuali.rice.coreservice.framework.parameter.ParameterService; 026import org.kuali.rice.core.web.format.BooleanFormatter; 027import org.kuali.rice.krad.UserSession; 028import org.kuali.rice.krad.service.KRADServiceLocator; 029import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 030import org.kuali.rice.krad.service.KualiModuleService; 031import org.kuali.rice.krad.service.ModuleService; 032import org.kuali.rice.krad.uif.util.ObjectPropertyUtils; 033 034import javax.servlet.ServletRequest; 035import javax.servlet.http.HttpServletRequest; 036import java.lang.reflect.Constructor; 037import java.lang.reflect.InvocationTargetException; 038import java.lang.reflect.Method; 039import java.security.GeneralSecurityException; 040import java.text.NumberFormat; 041import java.util.ArrayList; 042import java.util.Arrays; 043import java.util.Collection; 044import java.util.HashMap; 045import java.util.Iterator; 046import java.util.List; 047import java.util.Map; 048import java.util.Properties; 049import java.util.regex.Pattern; 050 051/** 052 * Miscellaneous Utility Methods 053 * 054 * @author Kuali Rice Team (rice.collab@kuali.org) 055 */ 056public final class KRADUtils { 057 private static KualiModuleService kualiModuleService; 058 059 private KRADUtils() { 060 throw new UnsupportedOperationException("do not call"); 061 } 062 063 public final static String getBusinessTitleForClass(Class<? extends Object> clazz) { 064 if (clazz == null) { 065 throw new IllegalArgumentException( 066 "The getBusinessTitleForClass method of KRADUtils requires a non-null class"); 067 } 068 String className = clazz.getSimpleName(); 069 070 StringBuffer label = new StringBuffer(className.substring(0, 1)); 071 for (int i = 1; i < className.length(); i++) { 072 if (Character.isLowerCase(className.charAt(i))) { 073 label.append(className.charAt(i)); 074 } else { 075 label.append(" ").append(className.charAt(i)); 076 } 077 } 078 return label.toString().trim(); 079 } 080 081 /** 082 * Picks off the filename from the full path. Takes care of different OS seperators. 083 */ 084 public final static List<String> getFileNameFromPath(List<String> fullFileNames) { 085 List<String> fileNameList = new ArrayList<String>(); 086 087 for (String fullFileName : fullFileNames) { 088 if (StringUtils.contains(fullFileName, "/")) { 089 fileNameList.add(StringUtils.substringAfterLast(fullFileName, "/")); 090 } else { 091 fileNameList.add(StringUtils.substringAfterLast(fullFileName, "\\")); 092 } 093 } 094 095 return fileNameList; 096 } 097 098 private static final KualiDecimal ONE_HUNDRED = new KualiDecimal("100.00"); 099 100 /** 101 * Convert the given monney amount into an interger string. Since the return string cannot have decimal point, 102 * multiplies the 103 * amount by 100 so the decimal places are not lost, for example, 320.15 is converted into 32015. 104 * 105 * @return an integer string of the given money amount through multiplicating by 100 and removing the fraction 106 * portion. 107 */ 108 public final static String convertDecimalIntoInteger(KualiDecimal decimalNumber) { 109 KualiDecimal decimalAmount = decimalNumber.multiply(ONE_HUNDRED); 110 NumberFormat formatter = NumberFormat.getIntegerInstance(); 111 String formattedAmount = formatter.format(decimalAmount); 112 113 return StringUtils.replace(formattedAmount, ",", ""); 114 } 115 116 public static Integer getIntegerValue(String numberStr) { 117 Integer numberInt = null; 118 try { 119 numberInt = new Integer(numberStr); 120 } catch (NumberFormatException nfe) { 121 Double numberDbl = new Double(numberStr); 122 numberInt = new Integer(numberDbl.intValue()); 123 } 124 return numberInt; 125 } 126 127 /** 128 * Attempt to coerce a String attribute value to the given propertyType. If the transformation can't be made, 129 * either because the propertyType is null or because the transformation required exceeds this method's very small 130 * bag of tricks, then null is returned. 131 * 132 * @param propertyType the Class to coerce the attributeValue to 133 * @param attributeValue the String value to coerce 134 * @return an instance of the propertyType class, or null the transformation can't be made. 135 */ 136 public static Object hydrateAttributeValue(Class<?> propertyType, String attributeValue){ 137 Object attributeValueObject = null; 138 if ( propertyType!=null && attributeValue!=null ) { 139 if (String.class.equals(propertyType)) { 140 // it's already a String 141 attributeValueObject = attributeValue; 142 } // KULRICE-6808: Kim Role Maintenance - Custom boolean role qualifier values are not being converted properly 143 else if (Boolean.class.equals(propertyType) || Boolean.TYPE.equals(propertyType)) { 144 attributeValueObject = Truth.strToBooleanIgnoreCase(attributeValue); 145 } else { 146 // try to create one with KRADUtils for other misc data types 147 attributeValueObject = KRADUtils.createObject(propertyType, new Class[]{String.class}, new Object[]{attributeValue}); 148 // if that didn't work, we'll get a null back 149 } 150 } 151 return attributeValueObject; 152 } 153 154 155 public static Object createObject(Class<?> clazz, Class<?>[] argumentClasses, Object[] argumentValues) { 156 if (clazz == null) { 157 return null; 158 } 159 if (argumentClasses.length == 1 && argumentClasses[0] == String.class) { 160 if (argumentValues.length == 1 && argumentValues[0] != null) { 161 if (clazz == String.class) { 162 // this means we're trying to create a String from a String 163 // don't new up Strings, it's a bad idea 164 return argumentValues[0]; 165 } else { 166 // maybe it's a type that supports valueOf? 167 Method valueOfMethod = null; 168 try { 169 valueOfMethod = clazz.getMethod("valueOf", String.class); 170 } catch (NoSuchMethodException e) { 171 // ignored 172 } 173 if (valueOfMethod != null) { 174 try { 175 return valueOfMethod.invoke(null, argumentValues[0]); 176 } catch (Exception e) { 177 // ignored 178 } 179 } 180 } 181 } 182 } 183 try { 184 Constructor<?> constructor = clazz.getConstructor(argumentClasses); 185 return constructor.newInstance(argumentValues); 186 } catch (Exception e) { 187 // ignored 188 } 189 return null; 190 } 191 192 public static String joinWithQuotes(List<String> list) { 193 if (list == null || list.size() == 0) 194 return ""; 195 196 return KRADConstants.SINGLE_QUOTE + 197 StringUtils.join(list.iterator(), KRADConstants.SINGLE_QUOTE + "," + KRADConstants.SINGLE_QUOTE) + 198 KRADConstants.SINGLE_QUOTE; 199 } 200 201 private static KualiModuleService getKualiModuleService() { 202 if (kualiModuleService == null) { 203 kualiModuleService = KRADServiceLocatorWeb.getKualiModuleService(); 204 } 205 return kualiModuleService; 206 } 207 208 /** 209 * TODO this method will probably need to be exposed in a public KRADUtils class as it is used 210 * by several different modules. That will have to wait until ModuleService and KualiModuleService are moved 211 * to core though. 212 */ 213 public static String getNamespaceCode(Class<? extends Object> clazz) { 214 ModuleService moduleService = getKualiModuleService().getResponsibleModuleService(clazz); 215 if (moduleService == null) { 216 return KRADConstants.DEFAULT_NAMESPACE; 217 } 218 return moduleService.getModuleConfiguration().getNamespaceCode(); 219 } 220 221 public static Map<String, String> getNamespaceAndComponentSimpleName(Class<? extends Object> clazz) { 222 Map<String, String> map = new HashMap<String, String>(); 223 map.put(KRADConstants.NAMESPACE_CODE, getNamespaceCode(clazz)); 224 map.put(KRADConstants.COMPONENT_NAME, getComponentSimpleName(clazz)); 225 return map; 226 } 227 228 public static Map<String, String> getNamespaceAndComponentFullName(Class<? extends Object> clazz) { 229 Map<String, String> map = new HashMap<String, String>(); 230 map.put(KRADConstants.NAMESPACE_CODE, getNamespaceCode(clazz)); 231 map.put(KRADConstants.COMPONENT_NAME, getComponentFullName(clazz)); 232 return map; 233 } 234 235 public static Map<String, String> getNamespaceAndActionClass(Class<? extends Object> clazz) { 236 Map<String, String> map = new HashMap<String, String>(); 237 map.put(KRADConstants.NAMESPACE_CODE, getNamespaceCode(clazz)); 238 map.put(KRADConstants.ACTION_CLASS, clazz.getName()); 239 return map; 240 } 241 242 private static String getComponentSimpleName(Class<? extends Object> clazz) { 243 return clazz.getSimpleName(); 244 } 245 246 private static String getComponentFullName(Class<? extends Object> clazz) { 247 return clazz.getName(); 248 } 249 250 /** 251 * Parses a string that is in map format (commas separating map entries, colon separates 252 * map key/value) to a new map instance 253 * 254 * @param parameter - string parameter to parse 255 * @return Map<String, String> instance populated from string parameter 256 */ 257 public static Map<String, String> convertStringParameterToMap(String parameter) { 258 Map<String, String> map = new HashMap<String, String>(); 259 260 if (StringUtils.isNotBlank(parameter)) { 261 if (StringUtils.contains(parameter, ",")) { 262 String[] fieldConversions = StringUtils.split(parameter, ","); 263 264 for (int i = 0; i < fieldConversions.length; i++) { 265 String fieldConversionStr = fieldConversions[i]; 266 if (StringUtils.isNotBlank(fieldConversionStr)) { 267 if (StringUtils.contains(fieldConversionStr, ":")) { 268 String[] fieldConversion = StringUtils.split(fieldConversionStr, ":"); 269 map.put(fieldConversion[0], fieldConversion[1]); 270 } else { 271 map.put(fieldConversionStr, fieldConversionStr); 272 } 273 } 274 } 275 } else if (StringUtils.contains(parameter, ":")) { 276 String[] fieldConversion = StringUtils.split(parameter, ":"); 277 map.put(fieldConversion[0], fieldConversion[1]); 278 } else { 279 map.put(parameter, parameter); 280 } 281 } 282 283 return map; 284 } 285 286 /** 287 * Parses a string that is in list format (commas separating list entries) to a new List instance 288 * 289 * @param parameter - string parameter to parse 290 * @return List<String> instance populated from string parameter 291 */ 292 public static List<String> convertStringParameterToList(String parameter) { 293 List<String> list = new ArrayList<String>(); 294 295 if (StringUtils.isNotBlank(parameter)) { 296 if (StringUtils.contains(parameter, ",")) { 297 String[] parameters = StringUtils.split(parameter, ","); 298 List arraysList = Arrays.asList(parameters); 299 list.addAll(arraysList); 300 } else { 301 list.add(parameter); 302 } 303 } 304 305 return list; 306 } 307 308 /** 309 * Translates characters in the given string like brackets that will cause 310 * problems with binding to characters that do not affect the binding 311 * 312 * @param key - string to translate 313 * @return String translated string 314 */ 315 public static String translateToMapSafeKey(String key) { 316 String safeKey = key; 317 318 safeKey = StringUtils.replace(safeKey, "[", "_"); 319 safeKey = StringUtils.replace(safeKey, "]", "_"); 320 321 return safeKey; 322 } 323 324 /** 325 * Builds a string from the given map by joining each entry with a comma and 326 * each key/value pair with a colon 327 * 328 * @param map - map instance to build string for 329 * @return String of map entries 330 */ 331 public static String buildMapParameterString(Map<String, String> map) { 332 String parameterString = ""; 333 334 for (Map.Entry<String, String> entry : map.entrySet()) { 335 if (StringUtils.isNotBlank(parameterString)) { 336 parameterString += ","; 337 } 338 339 parameterString += entry.getKey() + ":" + entry.getValue(); 340 } 341 342 return parameterString; 343 } 344 345 /** 346 * Parses the given string into a Map by splitting on the comma to get the 347 * map entries and within each entry splitting by colon to get the key/value 348 * pairs 349 * 350 * @param parameterString - string to parse into map 351 * @return Map<String, String> map from string 352 */ 353 public static Map<String, String> getMapFromParameterString(String parameterString) { 354 Map<String, String> map = new HashMap<String, String>(); 355 356 String[] entries = parameterString.split(","); 357 for (int i = 0; i < entries.length; i++) { 358 String[] keyValue = entries[i].split(":"); 359 if (keyValue.length != 2) { 360 throw new RuntimeException("malformed field conversion pair: " + Arrays.toString(keyValue)); 361 } 362 363 map.put(keyValue[0], keyValue[1]); 364 } 365 366 return map; 367 } 368 369 /** 370 * Retrieves value for the given parameter name in the request and attempts to convert to a Boolean using 371 * the <code>BooleanFormatter</code> 372 * 373 * @param request - servlet request containing parameters 374 * @param parameterName - name of parameter to retrieve value for 375 * @return Boolean set to value of parameter, or null if parameter was not found in request 376 */ 377 public static Boolean getRequestParameterAsBoolean(ServletRequest request, String parameterName) { 378 Boolean parameterValue = null; 379 380 String parameterValueStr = request.getParameter(parameterName); 381 if (StringUtils.isNotBlank(parameterValueStr)) { 382 parameterValue = (Boolean) new BooleanFormatter().convertFromPresentationFormat(parameterValueStr); 383 } 384 385 return parameterValue; 386 } 387 388 /** 389 * Translates the given Map of String keys and String array values to a Map 390 * of String key and values. If the String array contains more than one 391 * value, the single string is built by joining the values with the vertical 392 * bar character 393 * 394 * @param requestParameters - Map of request parameters to translate 395 * @return Map<String, String> translated Map 396 */ 397 public static Map<String, String> translateRequestParameterMap(Map<String, String[]> requestParameters) { 398 Map<String, String> parameters = new HashMap<String, String>(); 399 400 for (Map.Entry<String, String[]> parameter : requestParameters.entrySet()) { 401 String parameterValue = ""; 402 if (parameter.getValue().length > 1) { 403 parameterValue = StringUtils.join(parameter.getValue(), "|"); 404 } else { 405 parameterValue = parameter.getValue()[0]; 406 } 407 parameters.put(parameter.getKey(), parameterValue); 408 } 409 410 return parameters; 411 } 412 413 /** 414 * Retrieves parameter values from the request that match the requested 415 * names. In addition, based on the object class an authorization check is 416 * performed to determine if the values are secure and should be decrypted. 417 * If true, the value is decrypted before returning 418 * 419 * @param parameterNames - names of the parameters whose values should be retrieved 420 * from the request 421 * @param parentObjectClass - object class that contains the parameter names as properties 422 * and should be consulted for security checks 423 * @param requestParameters - all request parameters to pull from 424 * @return Map<String, String> populated with parameter name/value pairs 425 * pulled from the request 426 */ 427 public static Map<String, String> getParametersFromRequest(List<String> parameterNames, Class<?> parentObjectClass, 428 Map<String, String> requestParameters) { 429 Map<String, String> parameterValues = new HashMap<String, String>(); 430 431 for (Iterator<String> iter = parameterNames.iterator(); iter.hasNext(); ) { 432 String keyPropertyName = iter.next(); 433 434 if (requestParameters.get(keyPropertyName) != null) { 435 String keyValue = requestParameters.get(keyPropertyName); 436 437 // Check if this element was encrypted, if it was decrypt it 438 if (KRADServiceLocatorWeb.getDataObjectAuthorizationService() 439 .attributeValueNeedsToBeEncryptedOnFormsAndLinks(parentObjectClass, keyPropertyName)) { 440 try { 441 keyValue = StringUtils.removeEnd(keyValue, EncryptionService.ENCRYPTION_POST_PREFIX); 442 keyValue = CoreApiServiceLocator.getEncryptionService().decrypt(keyValue); 443 } catch (GeneralSecurityException e) { 444 throw new RuntimeException(e); 445 } 446 } 447 448 parameterValues.put(keyPropertyName, keyValue); 449 } 450 } 451 452 return parameterValues; 453 } 454 455 /** 456 * Builds a Map containing a key/value pair for each property given in the property names list, general 457 * security is checked to determine if the value needs to be encrypted along with applying formatting to 458 * the value 459 * 460 * @param propertyNames - list of property names to get key/value pairs for 461 * @param dataObject - object instance containing the properties for which the values will be pulled 462 * @return Map<String, String> containing entry for each property name with the property name as the map key 463 * and the property value as the value 464 */ 465 public static Map<String, String> getPropertyKeyValuesFromDataObject(List<String> propertyNames, 466 Object dataObject) { 467 Map<String, String> propertyKeyValues = new HashMap<String, String>(); 468 469 if (dataObject == null) { 470 return propertyKeyValues; 471 } 472 473 // iterate through properties and add a map entry for each 474 for (String propertyName : propertyNames) { 475 Object propertyValue = ObjectPropertyUtils.getPropertyValue(dataObject, propertyName); 476 if (propertyValue == null) { 477 propertyValue = StringUtils.EMPTY; 478 } 479 480 if (KRADServiceLocatorWeb.getDataObjectAuthorizationService() 481 .attributeValueNeedsToBeEncryptedOnFormsAndLinks(dataObject.getClass(), propertyName)) { 482 try { 483 if(CoreApiServiceLocator.getEncryptionService().isEnabled()) { 484 propertyValue = CoreApiServiceLocator.getEncryptionService().encrypt(propertyValue) + 485 EncryptionService.ENCRYPTION_POST_PREFIX; 486 } 487 } catch (GeneralSecurityException e) { 488 throw new RuntimeException("Exception while trying to encrypt value for key/value map.", e); 489 } 490 } 491 492 // TODO: need to apply formatting to return value once util class is ready 493 propertyKeyValues.put(propertyName, propertyValue.toString()); 494 } 495 496 return propertyKeyValues; 497 } 498 499 /** 500 * Utility method to convert a Map to a Properties object 501 * 502 * @param parameters - map to convert 503 * @return Properties object containing all the map entries 504 */ 505 public static Properties convertMapToProperties(Map<String, String> parameters) { 506 Properties properties = new Properties(); 507 508 if (parameters != null) { 509 for (Map.Entry<String, String> parameter : parameters.entrySet()) { 510 properties.put(parameter.getKey(), parameter.getValue()); 511 } 512 } 513 514 return properties; 515 } 516 517 /** 518 * Utility method to convert a Request Parameters Map to a Properties object 519 * 520 * <p> 521 * Multiple values for a parameter are joined together with comma delimiter 522 * </p> 523 * 524 * @param requestParameters - map to convert 525 * @return Properties object containing all the map entries 526 */ 527 public static Properties convertRequestMapToProperties(Map<String, String[]> requestParameters) { 528 Properties properties = new Properties(); 529 530 if (requestParameters != null) { 531 for (Map.Entry<String, String[]> parameter : requestParameters.entrySet()) { 532 String[] parameterValue = parameter.getValue(); 533 String parameterValueString = StringUtils.join(parameterValue, ","); 534 535 properties.put(parameter.getKey(), parameterValueString); 536 } 537 } 538 539 return properties; 540 } 541 542 public static boolean containsSensitiveDataPatternMatch(String fieldValue) { 543 if (StringUtils.isBlank(fieldValue)) { 544 return false; 545 } 546 ParameterService parameterService = CoreFrameworkServiceLocator.getParameterService(); 547 Collection<String> sensitiveDataPatterns = parameterService 548 .getParameterValuesAsString(KRADConstants.KNS_NAMESPACE, ParameterConstants.ALL_COMPONENT, 549 KRADConstants.SystemGroupParameterNames.SENSITIVE_DATA_PATTERNS); 550 for (String pattern : sensitiveDataPatterns) { 551 if (Pattern.compile(pattern).matcher(fieldValue).find()) { 552 return true; 553 } 554 } 555 return false; 556 } 557 558 /** 559 * Gets the UserSession object from the HttpServletRequest object's 560 * associated session. 561 * 562 * <p> 563 * In some cases (different threads) the UserSession cannot be retrieved 564 * from GlobalVariables but can still be accessed via the session object 565 * </p> 566 */ 567 public static final UserSession getUserSessionFromRequest(HttpServletRequest request) { 568 return (UserSession) request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY); 569 } 570 571 /** 572 * @return whether the deploy environment is production 573 */ 574 public static boolean isProductionEnvironment() { 575 return KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString( 576 KRADConstants.PROD_ENVIRONMENT_CODE_KEY) 577 .equalsIgnoreCase(KRADServiceLocator.getKualiConfigurationService().getPropertyValueAsString( 578 KRADConstants.ENVIRONMENT_KEY)); 579 } 580 581}