/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008 Sun Microsystems, Inc. * Portions copyright 2011 ForgeRock AS */ package org.opends.server.admin; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.forgerock.opendj.config.ConfigTestCase; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.Schema; import org.testng.Assert; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; @SuppressWarnings("javadoc") public class ValidateConfigDefinitionsTest extends ConfigTestCase { private static final String EOL = System.getProperty("line.separator"); @BeforeClass public void setup() throws Exception { disableClassValidationForProperties(); TestCfg.setUp(); } @AfterClass public void tearDown() { TestCfg.cleanup(); } @DataProvider Object[][] enumerateManageObjectDefns() throws Exception { TopCfgDefn topCfgDefn = TopCfgDefn.getInstance(); List> allCfgDefns = new ArrayList>(topCfgDefn.getAllChildren()); Object[][] params = new Object[allCfgDefns.size()][]; for (int i = 0; i < params.length; i++) { params[i] = new Object[] { allCfgDefns.get(i) }; } System.out.println(params.length); return params; } // Exceptions to config objects having a different objectclass private static final List CLASS_OBJECT_CLASS_EXCEPTIONS = Arrays.asList(new String[] { "org.opends.server.admin.std.meta.RootCfgDefn", "org.opends.server.admin.std.meta.GlobalCfgDefn", }); // TODO : does not work because can retrieve object class objects @Test(enabled = false, dataProvider = "enumerateManageObjectDefns") public void validateConfigObjectDefinitions(AbstractManagedObjectDefinition objectDef) { String objName = objectDef.getName(); StringBuilder errors = new StringBuilder(); Collection> allPropertyDefs = objectDef.getAllPropertyDefinitions(); LDAPProfile ldapProfile = LDAPProfile.getInstance(); String ldapObjectclassName = ldapProfile.getObjectClass(objectDef); if (ldapObjectclassName == null) { errors.append("There is no objectclass definition for configuration object " + objName); } else { String expectedObjectClass = "ds-cfg-" + objName; if (!ldapObjectclassName.equals(expectedObjectClass) && !CLASS_OBJECT_CLASS_EXCEPTIONS.contains(objectDef.getClass().getName())) { errors.append( "For config object " + objName + ", the LDAP objectclass must be " + expectedObjectClass + " instead of " + ldapObjectclassName).append(EOL + EOL); } } ObjectClass configObjectClass = Schema.getDefaultSchema().asNonStrictSchema(). getObjectClass(ldapObjectclassName.toLowerCase()); ; for (PropertyDefinition propDef : allPropertyDefs) { validatePropertyDefinition(objectDef, configObjectClass, propDef, errors); } if (errors.length() > 0) { Assert.fail("The configuration definition for " + objectDef.getName() + " has the following problems: " + EOL + errors.toString()); } } // Exceptions to properties ending in -class being exactly 'java-class'. private static final List CLASS_PROPERTY_EXCEPTIONS = Arrays.asList(new String[] { // e.g. "prop-name-ending-with-class" }); // Exceptions to properties ending in -enabled being exactly 'enabled'. private static final List ENABLED_PROPERTY_EXCEPTIONS = Arrays.asList(new String[] { "index-filter-analyzer-enabled", "subordinate-indexes-enabled" // e.g. "prop-name-ending-with-enabled" }); // Exceptions to properties not starting with the name of their config // object private static final List OBJECT_PREFIX_PROPERTY_EXCEPTIONS = Arrays.asList(new String[] { "backend-id", "plugin-type", "replication-server-id", "network-group-id", "workflow-id", "workflow-element-id", "workflow-element" // e.g. "prop-name-starting-with-object-prefix" }); private void validatePropertyDefinition(AbstractManagedObjectDefinition objectDef, ObjectClass configObjectClass, PropertyDefinition propDef, StringBuilder errors) { String objName = objectDef.getName(); String propName = propDef.getName(); // We want class properties to be exactly java-class if (propName.endsWith("-class") && !propName.equals("java-class") && !CLASS_PROPERTY_EXCEPTIONS.contains(propName)) { errors.append("The " + propName + " property on config object " + objName + " should probably be java-class. If not, then add " + propName + " to the CLASS_PROPERTY_EXCEPTIONS array in " + ValidateConfigDefinitionsTest.class.getName() + " to suppress" + " this warning."); } // We want enabled properties to be exactly enabled if (propName.endsWith("-enabled") && !ENABLED_PROPERTY_EXCEPTIONS.contains(propName)) { errors.append("The " + propName + " property on config object " + objName + " should probably be just 'enabled'. If not, then add " + propName + " to the ENABLED_PROPERTY_EXCEPTIONS array in " + ValidateConfigDefinitionsTest.class.getName() + " to suppress" + " this warning."); } // It's redundant for properties to be prefixed with the name of their // objecty if (propName.startsWith(objName) && !propName.equals(objName) && !OBJECT_PREFIX_PROPERTY_EXCEPTIONS.contains(propName)) { errors.append("The " + propName + " property on config object " + objName + " should not be prefixed with the name of the config object because" + " this is redundant. If you disagree, then add " + propName + " to the OBJECT_PREFIX_PROPERTY_EXCEPTIONS array in " + ValidateConfigDefinitionsTest.class.getName() + " to suppress" + " this warning."); } LDAPProfile ldapProfile = LDAPProfile.getInstance(); String ldapAttrName = ldapProfile.getAttributeName(objectDef, propDef); // LDAP attribute name is consistent with the property name String expectedLdapAttr = "ds-cfg-" + propName; if (!ldapAttrName.equals(expectedLdapAttr)) { errors.append( "For the " + propName + " property on config object " + objName + ", the LDAP attribute must be " + expectedLdapAttr + " instead of " + ldapAttrName).append(EOL + EOL); } Schema schema = Schema.getDefaultSchema(); AttributeType attrType = schema.getAttributeType(ldapAttrName.toLowerCase()); // LDAP attribute exists if (attrType == null) { errors.append( propName + " property on config object " + objName + " is declared" + " to use ldap attribute " + ldapAttrName + ", but this attribute is not in the schema ").append(EOL + EOL); } else { // LDAP attribute is multivalued if the property is multivalued if (propDef.hasOption(PropertyOption.MULTI_VALUED) && attrType.isSingleValue()) { errors.append( propName + " property on config object " + objName + " is declared" + " as multi-valued, but the corresponding ldap attribute " + ldapAttrName + " is declared as single-valued.").append(EOL + EOL); } if (configObjectClass != null) { // If it's mandatory in the schema, it must be mandatory on the // config property Set mandatoryAttributes = configObjectClass.getRequiredAttributes(); if (mandatoryAttributes.contains(attrType) && !propDef.hasOption(PropertyOption.MANDATORY)) { errors.append( propName + " property on config object " + objName + " is not declared" + " as mandatory even though the corresponding ldap attribute " + ldapAttrName + " is declared as mandatory in the schema.").append(EOL + EOL); } Set allowedAttributes = new HashSet(mandatoryAttributes); allowedAttributes.addAll(configObjectClass.getOptionalAttributes()); if (!allowedAttributes.contains(attrType)) { errors.append( propName + " property on config object " + objName + " has" + " the corresponding ldap attribute " + ldapAttrName + ", but this attribute is not an allowed attribute on the configuration " + " object's objectclass " + configObjectClass.getNameOrOID()).append(EOL + EOL); } } } } }