/*
* 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-2015 ForgeRock AS
*/
package org.forgerock.opendj.config;
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.ldap.schema.AttributeType;
import org.forgerock.opendj.ldap.schema.ObjectClass;
import org.forgerock.opendj.ldap.schema.Schema;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
@SuppressWarnings("javadoc")
@Test(singleThreaded = true)
public class ValidateConfigDefinitionsTest extends ConfigTestCase {
private static final String EOL = System.getProperty("line.separator");
@BeforeClass
public void setup() throws Exception {
TestCfg.setUp();
}
@AfterClass
public void tearDown() {
TestCfg.cleanup();
}
@DataProvider
Object[][] enumerateManageObjectDefns() throws Exception {
TopCfgDefn topCfgDefn = TopCfgDefn.getInstance();
List<AbstractManagedObjectDefinition<?, ?>> 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<String> CLASS_OBJECT_CLASS_EXCEPTIONS = Arrays.asList(new String[] {
"org.forgerock.opendj.config.std.meta.RootCfgDefn", "org.forgerock.opendj.config.std.meta.GlobalCfgDefn", });
// TODO : does not work because can't retrieve object class objects
@Test(enabled = false, dataProvider = "enumerateManageObjectDefns")
public void validateConfigObjectDefinitions(AbstractManagedObjectDefinition<?, ?> objectDef) {
String objName = objectDef.getName();
StringBuilder errors = new StringBuilder();
Collection<PropertyDefinition<?>> 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);
}
assertTrue(errors.length() != 0, "The configuration definition for " + objectDef.getName()
+ " has the following problems: " + EOL + errors);
}
/** Exceptions to properties ending in -class being exactly 'java-class'. */
private static final List<String> 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<String> 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<String> 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<AttributeType> 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<AttributeType> 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);
}
}
}
}
}