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 2009 Sun Microsystems, Inc. 015 * Portions copyright 2012-2016 ForgeRock AS. 016 */ 017 018package org.forgerock.opendj.ldap.controls; 019 020import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAPASSERT_CONTROL_BAD_OID; 021import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAPASSERT_INVALID_CONTROL_VALUE; 022import static com.forgerock.opendj.ldap.CoreMessages.ERR_LDAPASSERT_NO_CONTROL_VALUE; 023import static com.forgerock.opendj.util.StaticUtils.getExceptionMessage; 024 025import java.io.IOException; 026 027import org.forgerock.i18n.LocalizableMessage; 028import org.forgerock.opendj.io.ASN1; 029import org.forgerock.opendj.io.ASN1Reader; 030import org.forgerock.opendj.io.ASN1Writer; 031import org.forgerock.opendj.io.LDAP; 032import org.forgerock.opendj.ldap.ByteString; 033import org.forgerock.opendj.ldap.ByteStringBuilder; 034import org.forgerock.opendj.ldap.DecodeException; 035import org.forgerock.opendj.ldap.DecodeOptions; 036import org.forgerock.opendj.ldap.Filter; 037 038import org.forgerock.util.Reject; 039 040/** 041 * The assertion request control as defined in RFC 4528. The Assertion control 042 * allows a client to specify that a directory operation should only be 043 * processed if an assertion applied to the target entry of the operation is 044 * true. It can be used to construct "test and set", "test and clear", and other 045 * conditional operations. 046 * <p> 047 * The following excerpt shows how to check that no description exists on an 048 * entry before adding a description. 049 * 050 * <pre> 051 * Connection connection = ...; 052 * connection.bind(...); 053 * 054 * String entryDN = ...; 055 * ModifyRequest request = 056 * Requests.newModifyRequest(entryDN) 057 * .addControl(AssertionRequestControl.newControl( 058 * true, Filter.valueOf("!(description=*)"))) 059 * .addModification(ModificationType.ADD, "description", 060 * "Created using LDAP assertion control"); 061 * 062 * connection.modify(request); 063 * ... 064 * </pre> 065 * 066 * @see <a href="http://tools.ietf.org/html/rfc4528">RFC 4528 - Lightweight 067 * Directory Access Protocol (LDAP) Assertion Control </a> 068 */ 069public final class AssertionRequestControl implements Control { 070 /** The IANA-assigned OID for the LDAP assertion request control. */ 071 public static final String OID = "1.3.6.1.1.12"; 072 073 /** A decoder which can be used for decoding the LDAP assertion request control. */ 074 public static final ControlDecoder<AssertionRequestControl> DECODER = 075 new ControlDecoder<AssertionRequestControl>() { 076 077 @Override 078 public AssertionRequestControl decodeControl(final Control control, 079 final DecodeOptions options) throws DecodeException { 080 Reject.ifNull(control); 081 082 if (control instanceof AssertionRequestControl) { 083 return (AssertionRequestControl) control; 084 } 085 086 if (!control.getOID().equals(OID)) { 087 final LocalizableMessage message = 088 ERR_LDAPASSERT_CONTROL_BAD_OID.get(control.getOID(), OID); 089 throw DecodeException.error(message); 090 } 091 092 if (!control.hasValue()) { 093 // The response control must always have a value. 094 final LocalizableMessage message = ERR_LDAPASSERT_NO_CONTROL_VALUE.get(); 095 throw DecodeException.error(message); 096 } 097 098 try { 099 final ASN1Reader reader = ASN1.getReader(control.getValue()); 100 final Filter filter = LDAP.readFilter(reader); 101 return new AssertionRequestControl(control.isCritical(), filter); 102 } catch (final IOException e) { 103 throw DecodeException.error(ERR_LDAPASSERT_INVALID_CONTROL_VALUE 104 .get(getExceptionMessage(e)), e); 105 } 106 } 107 108 @Override 109 public String getOID() { 110 return OID; 111 } 112 }; 113 114 /** 115 * Creates a new assertion using the provided criticality and assertion 116 * filter. 117 * 118 * @param isCritical 119 * {@code true} if it is unacceptable to perform the operation 120 * without applying the semantics of this control, or 121 * {@code false} if it can be ignored. 122 * @param filter 123 * The assertion filter. 124 * @return The new control. 125 * @throws NullPointerException 126 * If {@code filter} was {@code null}. 127 */ 128 public static AssertionRequestControl newControl(final boolean isCritical, final Filter filter) { 129 return new AssertionRequestControl(isCritical, filter); 130 } 131 132 /** The assertion filter. */ 133 private final Filter filter; 134 135 private final boolean isCritical; 136 137 /** Prevent direct instantiation. */ 138 private AssertionRequestControl(final boolean isCritical, final Filter filter) { 139 Reject.ifNull(filter); 140 this.isCritical = isCritical; 141 this.filter = filter; 142 } 143 144 /** 145 * Returns the assertion filter. 146 * 147 * @return The assertion filter. 148 */ 149 public Filter getFilter() { 150 return filter; 151 } 152 153 @Override 154 public String getOID() { 155 return OID; 156 } 157 158 @Override 159 public ByteString getValue() { 160 final ByteStringBuilder buffer = new ByteStringBuilder(); 161 final ASN1Writer writer = ASN1.getWriter(buffer); 162 try { 163 LDAP.writeFilter(writer, filter); 164 return buffer.toByteString(); 165 } catch (final IOException ioe) { 166 // This should never happen unless there is a bug somewhere. 167 throw new RuntimeException(ioe); 168 } 169 } 170 171 @Override 172 public boolean hasValue() { 173 return true; 174 } 175 176 @Override 177 public boolean isCritical() { 178 return isCritical; 179 } 180 181 @Override 182 public String toString() { 183 final StringBuilder builder = new StringBuilder(); 184 builder.append("AssertionRequestControl(oid="); 185 builder.append(getOID()); 186 builder.append(", criticality="); 187 builder.append(isCritical()); 188 builder.append(", filter=\""); 189 builder.append(filter); 190 builder.append("\")"); 191 return builder.toString(); 192 } 193}