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