/* * 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 legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at 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 Copyright [year] [name of copyright owner]". * * Copyright 2008 Sun Microsystems, Inc. * Portions copyright 2011-2016 ForgeRock AS. * Portions Copyright 2024-2026 3A Systems, LLC */ package org.forgerock.opendj.config; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; 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> 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.forgerock.opendj.server.config.meta.RootCfgDefn", "org.forgerock.opendj.server.config.meta.GlobalCfgDefn", }); /** * Validates the naming conventions of every configuration definition and its properties. *

* This checks the parts of a definition that can be verified from the definitions and their * LDAP profiles alone: object class and attribute names follow the {@code ds-cfg-} convention, * {@code -class}/{@code -enabled} properties use the canonical names, and properties are not * redundantly prefixed with their object name. It intentionally does not validate a definition * against the live {@code ds-cfg-*} LDAP schema (required/optional attributes, single- vs * multi-valued, mandatory), because that schema is generated in the server module and is not on * this module's test class path. */ @Test(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); } } for (PropertyDefinition propDef : allPropertyDefs) { validatePropertyDefinition(objectDef, 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 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" }); /** Exceptions to LDAP attribute names not following the ds-cfg-<property> convention. */ private static final List LDAP_ATTRIBUTE_NAME_EXCEPTIONS = Arrays.asList(new String[] { // http-endpoint's authorization-mechanism property intentionally maps to this attribute. "ds-cfg-http-authorization-mechanism" // e.g. "ds-cfg-non-standard-attribute-name" }); private void validatePropertyDefinition(AbstractManagedObjectDefinition objectDef, 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) && !LDAP_ATTRIBUTE_NAME_EXCEPTIONS.contains(ldapAttrName)) { errors.append( "For the " + propName + " property on config object " + objName + ", the LDAP attribute must be " + expectedLdapAttr + " instead of " + ldapAttrName + ". If this deviation is intentional," + " then add " + ldapAttrName + " to the LDAP_ATTRIBUTE_NAME_EXCEPTIONS array in " + ValidateConfigDefinitionsTest.class.getName() + " to suppress this warning.").append(EOL + EOL); } } }