/** * Copyright (c) 2009--2014 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package com.redhat.rhn.manager.configuration; import com.redhat.rhn.common.conf.Config; import com.redhat.rhn.common.conf.ConfigDefaults; import com.redhat.rhn.common.validator.ValidatorError; import com.redhat.rhn.common.validator.ValidatorResult; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ConfigurationValidation * * Holds methods for validating a variety of configuration-related fields/values * * @version $Rev$ */ public class ConfigurationValidation { private ConfigurationValidation() { } /** * Validate incoming content. This attempts to do macro-substitution and complains * bitterly if it runs into problems: * * <ul> * <li>Can't find a function-name between the delimiters * <li>Don't like the function-name between the delimiters * <li>Don't like the arguments passed to the function-name between the delimiters * </ul> * * Returns an error list. An element is a Map with keys "key", "arg0", "arg1", "arg2". * The map values are used to I18N the resulting error message. * * @param content content of a TEXT config-file * @param macroStart start macro * @param macroEnd end macro * @return ValidatorResult. These will be reasons * why this content didn't pass. if result.isEmpty() - everything is OK */ public static ValidatorResult validateContent(String content, String macroStart, String macroEnd) { ValidatorResult result = new ValidatorResult(); // If no-file or macros aren't in the file, we're ok if (content == null || content.length() == 0) { return result; } boolean hasStart = content.indexOf(macroStart) >= 0; boolean hasEnd = content.indexOf(macroEnd) >= 0; // If no macros, we're done too if (!hasStart && !hasEnd) { return result; } // Macros might contain special reg-ex chars - escape them String escStart = ConfigurationValidation.regexEscape(macroStart); String escEnd = ConfigurationValidation.regexEscape(macroEnd); // Start-delim followed by zero or more whitespace followed by zero or more // characters followed by zero or more whitespace followed by end-delim String findMacroStr = escStart + "(.*?)" + escEnd; // One or more chars NOT parentheses$1, // followed by zero or one "( any-chars$3 ) $2", // followed by zero or one "= any-chars$5 $4" String macroStr = "([^()]+)(\\((.*?)\\))?\\s*(=(.*))?"; Pattern findMacro = Pattern.compile(findMacroStr, Pattern.MULTILINE + Pattern.DOTALL); Pattern macroPattern = Pattern.compile(macroStr, Pattern.DOTALL); Matcher matchMacro = findMacro.matcher(content); while (matchMacro.find()) { String seq = matchMacro.group(1).trim(); if (seq.length() == 0) { // Empty macro - skip continue; } Matcher parts = macroPattern.matcher(seq); if (parts.matches()) { String name = parts.group(1); String args = parts.group(3); // String deflt = parts.group(5); // Can't find a function-name if (name == null || name.trim().length() == 0) { result.addError(new ValidatorError( "configmanager.filedetails.content.no-macro-name", seq)); } // Function-name doesn't look like one we understand else if (!name.startsWith("rhn.system.")) { result.addError(new ValidatorError( "configmanager.filedetails.content.bad-macro-name", name.trim())); } // Arg-content must be word, whitespace values, or hyphens String regex = Config.get().getString( ConfigDefaults.CONFIG_MACRO_ARGUMENT_REGEX, "[\\w\\s-:]*"); if (args != null && !args.trim().matches(regex)) { result.addError(new ValidatorError( "configmanager.filedetails.content.bad-arg-content", name.trim(), args.trim())); } } else { // Something truly odd happened - complain bitterly result.addError(new ValidatorError( "configmanager.filedetails.content.bad-macro", matchMacro.group(0))); } } return result; } /** * Validate config-file pathname. The rules are pretty basic: * * <ul> * <li>MUST start with '/' * <li>CANNOT end with a '/' * <li>CANNOT be relative - no '..' anywhere * </ul> * That's it. * @param path pathname to be validated * @return a Validator Result. */ public static ValidatorResult validatePath(String path) { ValidatorResult result = new ValidatorResult(); if (path == null || path.length() == 0) { result.addError(new ValidatorError( "configmanager.filedetails.path.empty", path)); return result; } if (!path.startsWith("/")) { result.addError(new ValidatorError( "configmanager.filedetails.path.no-starting-slash", path)); } if (path.endsWith("/")) { result.addError(new ValidatorError( "configmanager.filedetails.path.has-ending-slash", path)); } if (path.indexOf("..") != -1) { result.addError(new ValidatorError( "configmanager.filedetails.path.has-relative-dirs", path)); } return result; } /** * Validate a user- or group-ID. Tests that it's a valid Linux u/gid * (positive int) * @param in string to be tested * @return true if valid, false otherwise */ public static boolean validateUGID(String in) { boolean valid = false; try { int i = Integer.parseInt(in); valid = (i > 0); } catch (Exception e) { valid = false; } return valid; } /** * Validate a user- or group-NAME. Tests that it's a valid Linux name * (starts w/alpha, followed by alphanumeric_-) * @param in string to be tested * @return true if valid, false otherwise */ public static boolean validateUserOrGroup(String in) { return (in != null && in.matches("^[a-zA-Z0-9_][a-zA-Z0-9_\\-]*$")); } /** * Escape a string that is going to be used to build a regex expression * @param inStr string of interest * @return string with non-whitespace and non-word characters escaped */ static String regexEscape(String inStr) { StringBuilder buff = new StringBuilder(""); for (int i = 0; i < inStr.length(); i++) { if (!inStr.substring(i, i + 1).matches("[\\s\\w]")) { buff.append("\\").append(inStr.charAt(i)); } else { buff.append(inStr.charAt(i)); } } return buff.toString(); } }