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}