/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 2005 Sun Microsystems Inc. All Rights Reserved * * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at * https://opensso.dev.java.net/public/CDDLv1.0.html or * opensso/legal/CDDLv1.0.txt * See the License for the specific language governing * permission and limitations under the License. * * When distributing Covered Code, include this CDDL * Header Notice in each file and include the License file * at opensso/legal/CDDLv1.0.txt. * If applicable, add the following below the CDDL Header, * with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * $Id: AttributeValidator.java,v 1.10 2009/11/03 00:06:31 hengming Exp $ * * Portions Copyrighted 2015 ForgeRock AS. */ package com.sun.identity.sm; import com.iplanet.am.util.SystemProperties; import com.iplanet.services.util.AMEncryption; import com.iplanet.ums.IUMSConstants; import com.iplanet.ums.validation.BooleanValidator; import com.iplanet.ums.validation.DNValidator; import com.iplanet.ums.validation.FloatValidator; import com.iplanet.ums.validation.MailAddressValidator; import com.iplanet.ums.validation.NumberValidator; import com.iplanet.ums.validation.URLValidator; import com.sun.identity.security.DecodeAction; import com.sun.identity.security.EncodeAction; import com.sun.identity.shared.Constants; import com.sun.identity.shared.debug.Debug; import java.security.AccessController; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** * The class AttributeValidator provides methods by which * ServiceConfig data to be stored in the Directory, can be validated against * the relevant Service Schema. The validator needs to check against the * relevant Schema to validate the attribute syntax and type. * */ class AttributeValidator { // Static variables static final MailAddressValidator mailValidator = new MailAddressValidator(); static final BooleanValidator boolValidator = new BooleanValidator(); static final NumberValidator numberValidator = new NumberValidator(); static final URLValidator urlValidator = new URLValidator(); static final FloatValidator floatValidator = new FloatValidator(); static final DNValidator dnValidator = new DNValidator(); static Debug debug = SMSEntry.debug; // Instance variables private AttributeSchemaImpl as; /** * Constructor * * @param as * the service schema which will be used to validate the * attribute values */ AttributeValidator(AttributeSchemaImpl as) { this.as = as; } /** * This method validates the syntax of the Attribute values against what it * is supposed to be in the ServiceSchema. * * @param values * Set of all the values for this attribute. * @param encodePassword * if true, the values will be encrypted if the attribute's * syntax is password * @return boolean true or false depending on whether the values are valid. * @throws SMSException */ private boolean validateSyntax(Set values, boolean encodePassword) throws SMSException { AttributeSchema.Syntax syntax = as.getSyntax(); if (syntax == null) return (true); if ((syntax.equals(AttributeSchema.Syntax.STRING)) || (syntax.equals(AttributeSchema.Syntax.PARAGRAPH)) || (syntax.equals(AttributeSchema.Syntax.SCRIPT)) || (syntax.equals(AttributeSchema.Syntax.URL)) || (syntax.equals(AttributeSchema.Syntax.XML)) || (syntax.equals(AttributeSchema.Syntax.BOOLEAN)) || (syntax.equals(AttributeSchema.Syntax.DATE))) { return (true); } if (syntax.equals(AttributeSchema.Syntax.EMAIL)) { Iterator it = values.iterator(); while (it.hasNext()) { String val = ((String) it.next()).trim(); /** * This condition is required because console is * passing a set of empty string. Without this check, * mailValidator will validate empty string for email * address and fail */ if ( (values.size() == 1) && (val.length() == 0) ) { break; } if (!mailValidator.validate(val)) { return (false); } } return (true); } if (syntax.equals(AttributeSchema.Syntax.PASSWORD) || syntax.equals(AttributeSchema.Syntax.ENCRYPTED_PASSWORD)) { if (encodePassword) { // Encrypt the passwords Set encValues = new HashSet(); Set remValues = new HashSet(); for (Iterator it = values.iterator(); it.hasNext();) { String value = (String) it.next(); try { encValues.add(AccessController .doPrivileged(new EncodeAction(value))); } catch (Throwable e) { debug.error("AttributeValidator: Unable to encode", e); encValues.add(value); } remValues.add(value); } values.removeAll(remValues); values.addAll(encValues); } return (true); } if (syntax.equals(AttributeSchema.Syntax.NUMERIC) || syntax.equals(AttributeSchema.Syntax.NUMBER)) { Iterator it = values.iterator(); while (it.hasNext()) { if (!numberValidator.validate((String) it.next())) { return (false); } } return (true); } if (syntax.equals(AttributeSchema.Syntax.PERCENT) || syntax.equals(AttributeSchema.Syntax.DECIMAL_NUMBER)) { Iterator it = values.iterator(); while (it.hasNext()) { if (!floatValidator.validate((String) it.next())) { return (false); } } return (true); } if (syntax.equals(AttributeSchema.Syntax.NUMBER_RANGE)) { Iterator it = values.iterator(); while (it.hasNext()) { String s = (String) it.next(); int i, start, end; try { i = Integer.parseInt(s); String startRange = as.getStartRange(); String endRange = as.getEndRange(); if ((startRange == null) && (endRange == null)) { return (true); } start = Integer.parseInt(startRange); end = Integer.parseInt(endRange); } catch (Exception e) { return (false); } if ((i < start) || (i > end)) { return (false); } } return (true); } if (syntax.equals(AttributeSchema.Syntax.DECIMAL_RANGE)) { Iterator it = values.iterator(); while (it.hasNext()) { String s = (String) it.next(); float f, start, end; try { f = Float.parseFloat(s); String startRange = as.getStartRange(); String endRange = as.getEndRange(); if ((startRange == null) && (endRange == null)) { return (true); } start = Float.parseFloat(startRange); end = Float.parseFloat(endRange); } catch (Exception e) { return (false); } if ((f < start) || (f > end)) { return (false); } } return (true); } if (syntax.equals(AttributeSchema.Syntax.DN)) { Iterator it = values.iterator(); while (it.hasNext()) { if (!dnValidator.validate((String) it.next())) return (false); } return (true); } // Doesn't fit any of these supported syntax?? String[] args = { as.getName() }; throw new SMSException(IUMSConstants.UMS_BUNDLE_NAME, "sms-invalid_attribute_syntax", args); } /** * This method validates the type of the Attribute values against what it is * supposed to be in the ServiceSchema. * * @param values * Set of all the values for this attribute. * @return boolean true or false depending on whether the values are valid. * @throws SMSException */ private boolean validateType(Set values, Map env) throws SMSException { String installTime = SystemProperties.get( Constants.SYS_PROPERTY_INSTALL_TIME, "false"); String[] array; AttributeSchema.Type type = as.getType(); if (type == null) return (true); if (type.equals(AttributeSchema.Type.SINGLE)) { if (values.size() > 1) { return (false); } else { return (true); } } if (type.equals(AttributeSchema.Type.LIST)) { int size = values.size(); int minValue = as.getMinValue(); int maxValue = as.getMaxValue(); if (!(minValue == -1 || maxValue == -1)) { if (size < minValue || size > maxValue) { return (false); } } return (true); } if (type.equals(AttributeSchema.Type.SINGLE_CHOICE)) { if (values.size() > 1) { return (false); } else { // we may not be able validate choice type attribute values // correctly during installation time or when importing // service configuration. if (installTime.equalsIgnoreCase("true")) { return true; } array = as.getChoiceValues(env); Iterator it = values.iterator(); String val = (it.hasNext()) ? (String) it.next() : null; if (val == null) { return (true); } for (int i = 0; i < array.length; i++) { if (array[i].equalsIgnoreCase(val)) { return (true); } } return (false); } } if (type.equals(AttributeSchema.Type.MULTIPLE_CHOICE)) { // we may not be able validate choice type attribute values // correctly during installation time or when importing // service configuration. if (installTime.equalsIgnoreCase("true")) { return true; } array = as.getChoiceValues(env); int size = values.size(); int minValue = as.getMinValue(); int maxValue = as.getMaxValue(); if (!(minValue == -1 || maxValue == -1)) { if (size < minValue || size > maxValue) { return (false); } } if (size == 0) { return (true); } if ((array == null) || (array.length == 0)) { return false; } Iterator it = values.iterator(); int arraySize = array.length; while (it.hasNext()) { boolean match = false; String value = (String) it.next(); for (int i = 0; i < arraySize; i++) { if (array[i].equalsIgnoreCase(value)) { match = true; break; } } if (!match) { return (false); } } return (true); } if (type.equals(AttributeSchema.Type.VALIDATOR)) { return (true); } if (type.equals(AttributeSchema.Type.SIGNATURE)) { return (true); } // Doesn't fit any of these supported type?? String[] args = { as.getName() }; throw new SMSException(IUMSConstants.UMS_BUNDLE_NAME, "sms-invalid_attribute_type", args); } /** * Validates a map of Attributes and values against Service Schema * definition. * * @param attrVals * a Set of attribute values * @param i18nFileName * Resource bundle file name * @param encodePassword * if true, the values will be encrypted if the attribute's * syntax is password * @return boolean true or false depending on whether the values are valid. * @throws SMSException * if values is invalid. */ boolean validate(Set attrVals, String i18nFileName, boolean encodePassword) throws SMSException { return validate(attrVals, i18nFileName, encodePassword, Collections.EMPTY_MAP); } /** * Validates a map of Attributes and values against Service Schema * definition. * * @param attrVals * a Set of attribute values * @param i18nFileName * Resource bundle file name * @param encodePassword * if true, the values will be encrypted if the attribute's * syntax is password * @param envParam * a Map of environment parameters * @return boolean true or false depending on whether the values are valid. * @throws SMSException * if values is invalid. */ boolean validate(Set attrVals, String i18nFileName, boolean encodePassword, Map envParam) throws SMSException { // removing old values, no need to validate if ((attrVals == null) || (attrVals.isEmpty())){ return true; } if (!validateType(attrVals, envParam) || !validateSyntax(attrVals, encodePassword)) { if (debug.messageEnabled()) { debug.message("Validation Failed for attribute: " + as.getName() + " value:" + attrVals + " Env Map: " + envParam); } if (i18nFileName != null) { String[] args = { as.getName(), i18nFileName, as.getI18NKey() }; throw (new InvalidAttributeValueException( IUMSConstants.UMS_BUNDLE_NAME, "sms-attribute-values-does-not-match-schema", args)); } else { String[] args = { as.getName() }; throw (new InvalidAttributeValueException( IUMSConstants.UMS_BUNDLE_NAME, "sms-attribute-values-does-not-match-schema", args)); } } return (true); } /** * This method checks if the attribute name (as given by the * AttributeSchema) is present, and if missings adds the defaults values. * * @param attrs * A map of the attributes and their values * @return A map which is a union of the attributes provided and default * attribute values */ Map inheritDefaults(Map attrs) { Set values = (Set) attrs.get(as.getName()); if (values == null) { // Inherit the default values attrs.put(as.getName(), as.getDefaultValues()); } else if (as.getSyntax().equals(AttributeSchema.Syntax.PASSWORD) || as.getSyntax().equals( AttributeSchema.Syntax.ENCRYPTED_PASSWORD)) { // Decrypt the password Set vals = new HashSet(); for (Iterator items = values.iterator(); items.hasNext();) { String tString = (String) items.next(); try { vals.add(AccessController.doPrivileged(new DecodeAction( tString))); } catch (Throwable e) { debug.error("AttributeValidator: Unable to decode", e); vals.add(tString); } } attrs.put(as.getName(), vals); } return (attrs); } /** * This method checks if attribute schema is of syntax password or * encoded_password, if so it decrypts the password when it is stored in the * cache. * * @param attrs * a Map of the attributes and their values * @return A map which is has replaced encrypted values with decrypted ones. */ Map decodeEncodedAttrs(Map attrs) { Set values = (Set) attrs.get(as.getName()); if (values == null) { return attrs; } if (as.getSyntax().equals(AttributeSchema.Syntax.PASSWORD) || as.getSyntax().equals( AttributeSchema.Syntax.ENCRYPTED_PASSWORD)) { // Decrypt the password Set vals = new HashSet(); for (Iterator items = values.iterator(); items.hasNext();) { String tString = (String) items.next(); try { vals.add(AccessController.doPrivileged(new DecodeAction( tString))); } catch (Throwable e) { debug.error("AttributeValidator: Unable to decode", e); vals.add(tString); } } attrs.put(as.getName(), vals); } return (attrs); } /** * Encodes attribute value if it is of syntax password or encoded_password. * * @param attrs Map of the attributes and their values * @param encryptObj Encryptor * @return A map which is has replaced values with encrypted ones. */ Map encodedAttrs(Map attrs, AMEncryption encryptObj) { Set values = (Set) attrs.get(as.getName()); if (values == null) { return attrs; } if (as.getSyntax().equals(AttributeSchema.Syntax.PASSWORD) || as.getSyntax().equals( AttributeSchema.Syntax.ENCRYPTED_PASSWORD)) { // Encrypt the password Set vals = new HashSet(); for (Iterator items = values.iterator(); items.hasNext();) { String tString = (String) items.next(); try { vals.add(AccessController.doPrivileged(new EncodeAction( tString, encryptObj))); } catch (Throwable e) { debug.error( "AttributeValidator.encodedAttrs: Unable to encode", e); vals.add(tString); } } attrs.put(as.getName(), vals); } return (attrs); } }