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 2010 Sun Microsystems, Inc.
015 * Portions Copyright 2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.ldap.controls;
018
019import static com.forgerock.opendj.ldap.CoreMessages.ERR_PERMISSIVE_MODIFY_CONTROL_BAD_OID;
020import static com.forgerock.opendj.ldap.CoreMessages.ERR_PERMISSIVE_MODIFY_INVALID_CONTROL_VALUE;
021
022import org.forgerock.i18n.LocalizableMessage;
023import org.forgerock.opendj.ldap.ByteString;
024import org.forgerock.opendj.ldap.DecodeException;
025import org.forgerock.opendj.ldap.DecodeOptions;
026
027import org.forgerock.util.Reject;
028
029/**
030 * The Microsoft defined permissive modify request control. The OID for this
031 * control is 1.2.840.113556.1.4.1413, and it does not have a value.
032 * <p>
033 * This control can only be used with LDAP modify requests. It changes the
034 * behavior of the modify operation as follows:
035 * <ul>
036 * <li>Attempts to add an attribute value which already exists will be ignored
037 * and will not cause an
038 * {@link org.forgerock.opendj.ldap.ResultCode#ATTRIBUTE_OR_VALUE_EXISTS
039 * AttributeValueExists} error result to be returned.
040 * <li>Attempts to delete an attribute value which does not exist will be
041 * ignored and will not cause an
042 * {@link org.forgerock.opendj.ldap.ResultCode#NO_SUCH_ATTRIBUTE
043 * NoSuchAttribute} error result to be returned.
044 * </ul>
045 * In other words, a modify request {@code add} modification <i>ensures</i> that
046 * the attribute contains the specified attribute value, and a {@code delete}
047 * modification <i>ensures</i> that the attribute does not contain the specified
048 * attribute value.
049 *
050 * <pre>
051 * String groupDN = ...;
052 * String memberDN = ...;
053 * Connection connection = ...;
054 *
055 * // Add a member to a static group, telling the directory server not to
056 * // complain if the member already belongs to the group.
057 * ModifyRequest request = Requests.newModifyRequest(groupDN)
058 *          .addControl(PermissiveModifyRequestControl.newControl(true))
059 *          .addModification(ModificationType.ADD, "member", memberDN);
060 * connection.modify(request);
061 * </pre>
062 */
063public final class PermissiveModifyRequestControl implements Control {
064    /** The OID for the permissive modify request control. */
065    public static final String OID = "1.2.840.113556.1.4.1413";
066
067    private static final PermissiveModifyRequestControl CRITICAL_INSTANCE =
068            new PermissiveModifyRequestControl(true);
069
070    private static final PermissiveModifyRequestControl NONCRITICAL_INSTANCE =
071            new PermissiveModifyRequestControl(false);
072
073    /** A decoder which can be used for decoding the permissive modify request control. */
074    public static final ControlDecoder<PermissiveModifyRequestControl> DECODER =
075            new ControlDecoder<PermissiveModifyRequestControl>() {
076
077                @Override
078                public PermissiveModifyRequestControl decodeControl(final Control control,
079                        final DecodeOptions options) throws DecodeException {
080                    Reject.ifNull(control);
081
082                    if (control instanceof PermissiveModifyRequestControl) {
083                        return (PermissiveModifyRequestControl) control;
084                    }
085
086                    if (!control.getOID().equals(OID)) {
087                        final LocalizableMessage message =
088                                ERR_PERMISSIVE_MODIFY_CONTROL_BAD_OID.get(control.getOID(), OID);
089                        throw DecodeException.error(message);
090                    }
091
092                    if (control.hasValue()) {
093                        final LocalizableMessage message =
094                                ERR_PERMISSIVE_MODIFY_INVALID_CONTROL_VALUE.get();
095                        throw DecodeException.error(message);
096                    }
097
098                    return control.isCritical() ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE;
099                }
100
101                @Override
102                public String getOID() {
103                    return OID;
104                }
105            };
106
107    /**
108     * Creates a new permissive modify request control having the provided
109     * criticality.
110     *
111     * @param isCritical
112     *            {@code true} if it is unacceptable to perform the operation
113     *            without applying the semantics of this control, or
114     *            {@code false} if it can be ignored.
115     * @return The new control.
116     */
117    public static PermissiveModifyRequestControl newControl(final boolean isCritical) {
118        return isCritical ? CRITICAL_INSTANCE : NONCRITICAL_INSTANCE;
119    }
120
121    private final boolean isCritical;
122
123    private PermissiveModifyRequestControl(final boolean isCritical) {
124        this.isCritical = isCritical;
125    }
126
127    @Override
128    public String getOID() {
129        return OID;
130    }
131
132    @Override
133    public ByteString getValue() {
134        return null;
135    }
136
137    @Override
138    public boolean hasValue() {
139        return false;
140    }
141
142    @Override
143    public boolean isCritical() {
144        return isCritical;
145    }
146
147    @Override
148    public String toString() {
149        final StringBuilder builder = new StringBuilder();
150        builder.append("PermissiveModifyRequestControl(oid=");
151        builder.append(getOID());
152        builder.append(", criticality=");
153        builder.append(isCritical());
154        builder.append(")");
155        return builder.toString();
156    }
157
158}