001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2011-2014 ForgeRock AS.
015 */
016
017package org.forgerock.opendj.ldap.schema;
018
019import org.forgerock.opendj.ldap.DN;
020import org.forgerock.opendj.ldap.Entry;
021import org.forgerock.opendj.ldap.LdapException;
022
023/**
024 * This class provides various schema validation policy options for controlling
025 * how entries should be validated against the directory schema.
026 */
027public final class SchemaValidationPolicy {
028    /**
029     * A call-back which will be called during DIT structure rule schema
030     * validation in order to retrieve the parent of the entry being validated.
031     */
032    public static interface EntryResolver {
033        /**
034         * Returns the named entry in order to enforce DIT structure rules.
035         *
036         * @param dn
037         *            The name of the entry to be returned.
038         * @return The named entry.
039         * @throws LdapException
040         *             If the entry could not be retrieved.
041         */
042        Entry getEntry(DN dn) throws LdapException;
043    }
044
045    /**
046     * An enumeration of the possible actions which can be performed when a
047     * schema validation failure is encountered.
048     */
049    public static enum Action {
050        /**
051         * Schema validation will not be performed.
052         */
053        IGNORE,
054
055        /**
056         * Schema validation will be performed, but failures will not cause the
057         * overall validation to fail. Error messages will be returned.
058         */
059        WARN,
060
061        /**
062         * Schema validation will be performed and failures will cause the
063         * overall validation to fail. Error messages will be returned.
064         */
065        REJECT;
066
067        private Action() {
068            // Nothing to do.
069        }
070
071        /**
072         * Returns {@code true} if this policy is {@code IGNORE}.
073         *
074         * @return {@code true} if this policy is {@code IGNORE}.
075         */
076        public boolean isIgnore() {
077            return this == IGNORE;
078        }
079
080        /**
081         * Returns {@code true} if this policy is {@code REJECT}.
082         *
083         * @return {@code true} if this policy is {@code REJECT}.
084         */
085        public boolean isReject() {
086            return this == REJECT;
087        }
088
089        /**
090         * Returns {@code true} if this policy is {@code WARN}.
091         *
092         * @return {@code true} if this policy is {@code WARN}.
093         */
094        public boolean isWarn() {
095            return this == WARN;
096        }
097
098        /**
099         * Returns {@code true} if this policy is {@code WARN} or {@code REJECT}
100         * .
101         *
102         * @return {@code true} if this policy is {@code WARN} or {@code REJECT}
103         *         .
104         */
105        public boolean needsChecking() {
106            return this != IGNORE;
107        }
108    }
109
110    /**
111     * Creates a copy of the provided schema validation policy.
112     *
113     * @param policy
114     *            The policy to be copied.
115     * @return The copy of the provided schema validation policy.
116     */
117    public static SchemaValidationPolicy copyOf(final SchemaValidationPolicy policy) {
118        return defaultPolicy().assign(policy);
119    }
120
121    /**
122     * Creates a new schema validation policy with default settings. More
123     * specifically:
124     * <ul>
125     * <li>Entries not having a single structural object class will be rejected
126     * <li>Entries having attributes which are not permitted by its object
127     * classes or DIT content rule (if present) will be rejected
128     * <li>Entries not conforming to name forms will be rejected
129     * <li>DIT structure rules will not be ignored
130     * </ul>
131     *
132     * @return The new schema validation policy.
133     */
134    public static SchemaValidationPolicy defaultPolicy() {
135        return new SchemaValidationPolicy();
136    }
137
138    /**
139     * Creates a new schema validation policy which will not perform any schema
140     * validation.
141     *
142     * @return The new schema validation policy.
143     */
144    public static SchemaValidationPolicy ignoreAll() {
145        return new SchemaValidationPolicy().checkAttributesAndObjectClasses(Action.IGNORE)
146                .checkAttributeValues(Action.IGNORE).checkDITContentRules(Action.IGNORE)
147                .checkNameForms(Action.IGNORE).requireSingleStructuralObjectClass(Action.IGNORE);
148    }
149
150    private Action checkNameForms = Action.REJECT;
151    private Action checkDITStructureRules = Action.IGNORE;
152    private Action checkDITContentRules = Action.REJECT;
153    private Action requireSingleStructuralObjectClass = Action.REJECT;
154    private Action checkAttributesAndObjectClasses = Action.REJECT;
155    private Action checkAttributeValues = Action.REJECT;
156    private EntryResolver checkDITStructureRulesEntryResolver;
157
158    /** Prevent direct instantiation. */
159    private SchemaValidationPolicy() {
160        // Nothing to do.
161    }
162
163    /**
164     * Returns the policy for verifying that the user attributes in an entry
165     * conform to its object classes. More specifically, an entry must contain
166     * all required user attributes, and must not contain any user attributes
167     * which are not declared as required or optional by its object classes.
168     * <p>
169     * By default entries which have missing or additional user attributes will
170     * be rejected.
171     *
172     * @return The policy for verifying that the user attributes in an entry
173     *         conform to its object classes.
174     */
175    public Action checkAttributesAndObjectClasses() {
176        return checkAttributesAndObjectClasses;
177    }
178
179    /**
180     * Specifies the policy for verifying that the user attributes in an entry
181     * conform to its object classes. More specifically, an entry must contain
182     * all required user attributes, and must not contain any user attributes
183     * which are not declared as required or optional by its object classes.
184     * <p>
185     * By default entries which have missing or additional user attributes will
186     * be rejected.
187     *
188     * @param policy
189     *            The policy for verifying that the user attributes in an entry
190     *            conform to its object classes.
191     * @return A reference to this {@code SchemaValidationPolicy}.
192     */
193    public SchemaValidationPolicy checkAttributesAndObjectClasses(final Action policy) {
194        this.checkAttributesAndObjectClasses = policy;
195        return this;
196    }
197
198    /**
199     * Returns the policy for verifying that the user attributes in an entry
200     * conform to their associated attribute type descriptions. This may
201     * include:
202     * <ul>
203     * <li>checking that there is at least one value
204     * <li>checking that single-valued attributes contain only a single value
205     * <li>checking that there are no duplicate values according to the
206     * attribute's default equality matching rule
207     * <li>checking that attributes which require BER encoding specify the
208     * {@code ;binary} attribute option
209     * <li>checking that the values are valid according to the attribute's
210     * syntax.
211     * </ul>
212     * Schema validation implementations specify exactly which of the above
213     * checks will be performed.
214     * <p>
215     * By default entries which have invalid attribute values will be rejected.
216     *
217     * @return The policy for verifying that the user attributes in an entry
218     *         conform to their associated attribute type descriptions.
219     */
220    public Action checkAttributeValues() {
221        return checkAttributeValues;
222    }
223
224    /**
225     * Specifies the policy for verifying that the user attributes in an entry
226     * conform to their associated attribute type descriptions. This may
227     * include:
228     * <ul>
229     * <li>checking that there is at least one value
230     * <li>checking that single-valued attributes contain only a single value
231     * <li>checking that there are no duplicate values according to the
232     * attribute's default equality matching rule
233     * <li>checking that attributes which require BER encoding specify the
234     * {@code ;binary} attribute option
235     * <li>checking that the values are valid according to the attribute's
236     * syntax.
237     * </ul>
238     * Schema validation implementations specify exactly which of the above
239     * checks will be performed.
240     * <p>
241     * By default entries which have invalid attribute values will be rejected.
242     *
243     * @param policy
244     *            The policy for verifying that the user attributes in an entry
245     *            conform to their associated attribute type descriptions.
246     * @return A reference to this {@code SchemaValidationPolicy}.
247     */
248    public SchemaValidationPolicy checkAttributeValues(final Action policy) {
249        this.checkAttributeValues = policy;
250        return this;
251    }
252
253    /**
254     * Returns the policy for validating entries against content rules defined
255     * in the schema.
256     * <p>
257     * By default content rules will be ignored during validation.
258     *
259     * @return The policy for validating entries against content rules defined
260     *         in the schema.
261     */
262    public Action checkDITContentRules() {
263        return checkDITContentRules;
264    }
265
266    /**
267     * Specifies the policy for validating entries against content rules defined
268     * in the schema.
269     * <p>
270     * By default content rules will be ignored during validation.
271     *
272     * @param policy
273     *            The policy for validating entries against content rules
274     *            defined in the schema.
275     * @return A reference to this {@code SchemaValidationPolicy}.
276     */
277    public SchemaValidationPolicy checkDITContentRules(final Action policy) {
278        this.checkDITContentRules = policy;
279        return this;
280    }
281
282    /**
283     * Returns the policy for validating entries against structure rules defined
284     * in the schema.
285     * <p>
286     * By default structure rules will be ignored during validation.
287     *
288     * @return The policy for validating entries against structure rules defined
289     *         in the schema.
290     */
291    public Action checkDITStructureRules() {
292        return checkDITStructureRules;
293    }
294
295    /**
296     * Specifies the policy for validating entries against structure rules
297     * defined in the schema.
298     * <p>
299     * By default structure rules will be ignored during validation.
300     *
301     * @param policy
302     *            The policy for validating entries against structure rules
303     *            defined in the schema.
304     * @param resolver
305     *            The parent entry resolver which should be used for retrieving
306     *            the parent entry during DIT structure rule validation.
307     * @return A reference to this {@code SchemaValidationPolicy}.
308     * @throws IllegalArgumentException
309     *             If {@code resolver} was {@code null} and
310     *             {@code checkDITStructureRules} is either {@code WARN} or
311     *             {@code REJECT}.
312     */
313    public SchemaValidationPolicy checkDITStructureRules(final Action policy,
314            final EntryResolver resolver) {
315        if (checkDITStructureRules.needsChecking() && resolver == null) {
316            throw new IllegalArgumentException(
317                    "Validation of structure rules enabled by resolver was null");
318        }
319        this.checkDITStructureRules = policy;
320        this.checkDITStructureRulesEntryResolver = resolver;
321        return this;
322    }
323
324    /**
325     * Returns parent entry resolver which should be used for retrieving the
326     * parent entry during DIT structure rule validation.
327     * <p>
328     * By default no resolver is defined because structure rules will be ignored
329     * during validation.
330     *
331     * @return The parent entry resolver which should be used for retrieving the
332     *         parent entry during DIT structure rule validation.
333     */
334    public EntryResolver checkDITStructureRulesEntryResolver() {
335        return checkDITStructureRulesEntryResolver;
336    }
337
338    /**
339     * Returns the policy for validating entries against name forms defined in
340     * the schema.
341     * <p>
342     * By default name forms will be ignored during validation.
343     *
344     * @return The policy for validating entries against name forms defined in
345     *         the schema.
346     */
347    public Action checkNameForms() {
348        return checkNameForms;
349    }
350
351    /**
352     * Specifies the policy for validating entries against name forms defined in
353     * the schema.
354     * <p>
355     * By default name forms will be ignored during validation.
356     *
357     * @param policy
358     *            The policy for validating entries against name forms defined
359     *            in the schema.
360     * @return A reference to this {@code SchemaValidationPolicy}.
361     */
362    public SchemaValidationPolicy checkNameForms(final Action policy) {
363        this.checkNameForms = policy;
364        return this;
365    }
366
367    /**
368     * Returns the policy for verifying that entries have only a single
369     * structural object class.
370     * <p>
371     * By default entries which do not have a structural object class or which
372     * have more than one structural object class will be rejected.
373     *
374     * @return The policy for checking that entries have one and only one
375     *         structural object class.
376     */
377    public Action requireSingleStructuralObjectClass() {
378        return requireSingleStructuralObjectClass;
379    }
380
381    /**
382     * Specifies the policy for verifying that entries have only a single
383     * structural object class.
384     * <p>
385     * By default entries which do not have a structural object class or which
386     * have more than one structural object class will be rejected.
387     *
388     * @param policy
389     *            The policy for checking that entries have one and only one
390     *            structural object class.
391     * @return A reference to this {@code SchemaValidationPolicy}.
392     */
393    public SchemaValidationPolicy requireSingleStructuralObjectClass(final Action policy) {
394        this.requireSingleStructuralObjectClass = policy;
395        return this;
396    }
397
398    /**
399     * Returns a strict view of the provided schema if the this policy is
400     * configured to check attributes and object class, or a non-strict view of
401     * the schema if not.
402     *
403     * @param schema
404     *            The schema to be adapted according to this policy.
405     * @return A strict or non-strict view of {@code schema} depending on
406     *         {@link #checkAttributesAndObjectClasses()}.
407     */
408    public Schema adaptSchemaForValidation(final Schema schema) {
409        return checkAttributesAndObjectClasses().needsChecking() ? schema.asStrictSchema() : schema
410                .asNonStrictSchema();
411    }
412
413    /** Assigns the provided options to this set of options. */
414    SchemaValidationPolicy assign(final SchemaValidationPolicy policy) {
415        this.checkAttributeValues = policy.checkAttributeValues;
416        this.checkNameForms = policy.checkNameForms;
417        this.checkAttributesAndObjectClasses = policy.checkAttributesAndObjectClasses;
418        this.checkDITContentRules = policy.checkDITContentRules;
419        this.checkDITStructureRules = policy.checkDITStructureRules;
420        this.checkDITStructureRulesEntryResolver = policy.checkDITStructureRulesEntryResolver;
421        this.requireSingleStructuralObjectClass = policy.requireSingleStructuralObjectClass;
422        return this;
423    }
424
425}