/* * 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 2016 ForgeRock AS. */ package org.opends.server.util; import static org.opends.messages.SchemaMessages.*; import static org.opends.server.util.ServerConstants.SCHEMA_PROPERTY_FILENAME; import static org.opends.server.schema.SchemaConstants.SYNTAX_AUTH_PASSWORD_OID; import static org.opends.server.schema.SchemaConstants.SYNTAX_USER_PASSWORD_OID; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1; import org.forgerock.opendj.ldap.ByteString; import org.forgerock.opendj.ldap.ResultCode; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.ObjectClass; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldap.schema.SchemaBuilder; import org.forgerock.opendj.ldap.schema.SchemaElement; import org.opends.server.core.ServerContext; import org.opends.server.types.DirectoryException; import com.forgerock.opendj.util.SubstringReader; /** Utility methods related to schema. */ public class SchemaUtils { /** Private constructor to prevent instantiation. */ private SchemaUtils() { // No implementation required. } /** Represents a password type, including a "not a password" value. */ public enum PasswordType { /** Auth Password. */ AUTH_PASSWORD, /** User Password. */ USER_PASSWORD, /** Not a password. */ NOT_A_PASSWORD } /** * Checks if the provided attribute type contains a password. * * @param attrType * The attribute type to check. * @return a PasswordTypeCheck result */ public static PasswordType checkPasswordType(AttributeType attrType) { final String syntaxOID = attrType.getSyntax().getOID(); if (syntaxOID.equals(SYNTAX_AUTH_PASSWORD_OID)) { return PasswordType.AUTH_PASSWORD; } else if (attrType.hasName("userPassword") || syntaxOID.equals(SYNTAX_USER_PASSWORD_OID)) { return PasswordType.USER_PASSWORD; } return PasswordType.NOT_A_PASSWORD; } /** * Retrieves an attribute value containing a representation of the provided * boolean value. * * @param b The boolean value for which to retrieve the attribute value. * * @return The attribute value created from the provided boolean value. */ public static ByteString createBooleanValue(boolean b) { return b ? ServerConstants.TRUE_VALUE : ServerConstants.FALSE_VALUE; } /** * Retrieves the definition string used to create the provided schema element and including the * X-SCHEMA-FILE extension. * * @param element * The schema element. * @return The definition string used to create the schema element including the X-SCHEMA-FILE * extension. */ public static String getElementDefinitionWithFileName(SchemaElement element) { final String definition = element.toString(); return addSchemaFileToElementDefinitionIfAbsent(definition, SchemaUtils.getElementSchemaFile(element)); } /** * Returns the origin of the provided schema element. * * @param element * The schema element. * @return the origin of the schema element as defined in the extra properties. */ public static String getElementOrigin(SchemaElement element) { return getElementPropertyAsSingleValue(element, ServerConstants.SCHEMA_PROPERTY_ORIGIN); } /** * Returns the single value of the provided extra property for the provided schema element. * * @param element * The schema element. * @param property * The name of property to retrieve. * @return the single value of the extra property */ public static String getElementPropertyAsSingleValue(SchemaElement element, String property) { List values = element.getExtraProperties().get(property); return values != null && !values.isEmpty() ? values.get(0) : null; } /** * Returns the schema file of the provided schema element. * * @param element * The schema element. * @return the schema file of schema element. */ public static String getElementSchemaFile(SchemaElement element) { return getElementPropertyAsSingleValue(element, ServerConstants.SCHEMA_PROPERTY_FILENAME); } /** * Returns a new collection with the result of calling {@link ObjectClass#getNameOrOID()} on each * element of the provided collection. * * @param objectClasses * the schema elements on which to act * @return a new collection comprised of the names or OIDs of each element */ public static Collection getNameOrOIDsForOCs(Collection objectClasses) { Set results = new HashSet<>(objectClasses.size()); for (ObjectClass objectClass : objectClasses) { results.add(objectClass.getNameOrOID()); } return results; } /** * Returns a new collection with the result of calling {@link AttributeType#getNameOrOID()} on * each element of the provided collection. * * @param attributeTypes * the schema elements on which to act * @return a new collection comprised of the names or OIDs of each element */ public static Collection getNameOrOIDsForATs(Collection attributeTypes) { Set results = new HashSet<>(attributeTypes.size()); for (AttributeType attrType : attributeTypes) { results.add(attrType.getNameOrOID()); } return results; } /** * Returns the new updated attribute type with the provided extra property and its values. * * @param serverContext * the server context * @param attributeType * attribute type to update * @param property * the property to set * @param values * the values to set * @return the new updated attribute type */ public static AttributeType getNewAttributeTypeWithProperty(ServerContext serverContext, AttributeType attributeType, String property, String...values) { SchemaBuilder schemaBuilder = new SchemaBuilder(serverContext != null ? serverContext.getSchema() : Schema.getDefaultSchema()); AttributeType.Builder builder = schemaBuilder.buildAttributeType(attributeType).removeExtraProperty(property, (String) null); if (values != null && values.length > 0) { builder.extraProperties(property, values); return builder.addToSchemaOverwrite().toSchema().getAttributeType(attributeType.getNameOrOID()); } return attributeType; } /** * Adds the provided schema file to the provided schema element definition. * * @param definition * The schema element definition * @param schemaFile * The name of the schema file to include in the definition * @return The definition string of the element * including the X-SCHEMA-FILE extension. */ public static String addSchemaFileToElementDefinitionIfAbsent(String definition, String schemaFile) { if (schemaFile != null && !definition.contains(SCHEMA_PROPERTY_FILENAME)) { int pos = definition.lastIndexOf(')'); return definition.substring(0, pos).trim() + " " + SCHEMA_PROPERTY_FILENAME + " '" + schemaFile + "' )"; } return definition; } /** * Parses the schema file (value of X-SCHEMA-FILE extension) from the provided schema element * definition. *

* It expects a single value for the X-SCHEMA-FILE extension, e.g.: * "X-SCHEMA-FILE '99-user.ldif'", as there is no sensible meaning for multiple values. * * @param definition * The definition of a schema element * @return the value of the schema file or {@code null} if the X-SCHEMA-FILE extension is not * present in the definition * @throws DirectoryException * If an error occurs while parsing the schema element definition */ public static String parseSchemaFileFromElementDefinition(String definition) throws DirectoryException { int pos = definition.lastIndexOf(SCHEMA_PROPERTY_FILENAME); if (pos == -1) { return null; } SubstringReader reader = new SubstringReader(definition); reader.read(pos + SCHEMA_PROPERTY_FILENAME.length()); int length = 0; reader.skipWhitespaces(); reader.mark(); try { // Accept both a quoted value or an unquoted value char c = reader.read(); if (c == '\'') { reader.mark(); // Parse until the closing quote. while (reader.read() != '\'') { length++; } } else { // Parse until the next space. do { length++; } while (reader.read() != ' '); } reader.reset(); return reader.read(length); } catch (final StringIndexOutOfBoundsException e) { // TODO : write the correct message = Error when trying to parse the schema file from a schema // element definition throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, LocalizableMessage.raw("")); } } /** * Returns the OID from the provided attribute type definition, assuming the definition is valid. *

* This method does not perform any check. * * @param definition * The definition of an attribute type, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseAttributeTypeOID(String definition) throws DirectoryException { return parseOID(definition, ERR_PARSING_ATTRIBUTE_TYPE_OID); } /** * Returns the OID from the provided object class definition, assuming the definition is valid. *

* This method does not perform any check. * * @param definition * The definition of a object class, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseObjectClassOID(String definition) throws DirectoryException { return parseOID(definition, ERR_PARSING_OBJECTCLASS_OID); } /** * Returns the OID from the provided name form definition, assuming the definition is valid. *

* This method does not perform any check. * * @param definition * The definition of a name form, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseNameFormOID(String definition) throws DirectoryException { return parseOID(definition, ERR_PARSING_NAME_FORM_OID); } /** * Returns the OID from the provided DIT content rule definition, assuming the definition is valid. *

* This method does not perform any check. * * @param definition * The definition of a DIT content rule, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseDITContentRuleOID(String definition) throws DirectoryException { return parseOID(definition, ERR_PARSING_DIT_CONTENT_RULE_OID); } /** * Returns the ruleID from the provided DIT structure rule definition, assuming the definition is * valid. *

* This method does not perform any check. * * @param definition * The definition of a DIT structure rule, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static int parseRuleID(String definition) throws DirectoryException { // Reuse code of parseOID, even though this is not an OID return Integer.parseInt(parseOID(definition, ERR_PARSING_DIT_STRUCTURE_RULE_RULEID)); } /** * Returns the OID from the provided matching rule use definition, assuming the definition is valid. *

* This method does not perform any check. * * @param definition * The definition of a matching rule use, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseMatchingRuleUseOID(String definition) throws DirectoryException { return parseOID(definition, ERR_PARSING_MATCHING_RULE_USE_OID); } /** * Returns the OID from the provided syntax definition, assuming the definition is valid. *

* This method does not perform any check. * * @param definition * The definition of a syntax, assumed to be valid * @return the OID, which is never {@code null} * @throws DirectoryException * If a problem occurs while parsing the definition */ public static String parseSyntaxOID(String definition) throws DirectoryException { return parseOID(definition, ERR_PARSING_LDAP_SYNTAX_OID); } /** * Returns the OID from the provided definition, using the provided message if an error occurs. * * @param definition * The definition of a schema element * @param parsingErrorMsg * Error message to use in case of failure (should be related to * the specific schema element parsed) * @return the OID corresponding to the definition * @throws DirectoryException * If the parsing of the definition fails * * */ public static String parseOID(String definition, Arg1 parsingErrorMsg) throws DirectoryException { try { int pos = 0; int length = definition.length(); // Skip over any leading whitespace. while (pos < length && (definition.charAt(pos) == ' ')) { pos++; } // Skip the open parenthesis. pos++; // Skip over any spaces immediately following the opening parenthesis. while (pos < length && definition.charAt(pos) == ' ') { pos++; } // The next set of characters must be the OID. int oidStartPos = pos; while (pos < length && definition.charAt(pos) != ' ' && definition.charAt(pos) != ')') { pos++; } return definition.substring(oidStartPos, pos); } catch (IndexOutOfBoundsException e) { throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, parsingErrorMsg.get(definition), e); } } }