001/* 002 * Copyright 2013-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2013-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2013-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.experimental; 037 038 039 040import com.unboundid.asn1.ASN1Element; 041import com.unboundid.asn1.ASN1Integer; 042import com.unboundid.asn1.ASN1OctetString; 043import com.unboundid.asn1.ASN1Sequence; 044import com.unboundid.ldap.sdk.Control; 045import com.unboundid.ldap.sdk.DecodeableControl; 046import com.unboundid.ldap.sdk.LDAPException; 047import com.unboundid.ldap.sdk.ResultCode; 048import com.unboundid.ldap.sdk.SearchResult; 049import com.unboundid.util.Debug; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.NotNull; 052import com.unboundid.util.Nullable; 053import com.unboundid.util.StaticUtils; 054import com.unboundid.util.ThreadSafety; 055import com.unboundid.util.ThreadSafetyLevel; 056 057import static com.unboundid.ldap.sdk.experimental.ExperimentalMessages.*; 058 059 060 061/** 062 * This class provides support for a control that may be used to poll an Active 063 * Directory Server for information about changes that have been processed. Use 064 * of this control is documented at 065 * <A HREF="http://support.microsoft.com/kb/891995"> 066 * http://support.microsoft.com/kb/891995</A> and at 067 * <A HREF="http://msdn.microsoft.com/en-us/library/ms677626.aspx"> 068 * http://msdn.microsoft.com/en-us/library/ms677626.aspx</A>. The control OID 069 * and value format are described at 070 * <A HREF="http://msdn.microsoft.com/en-us/library/aa366978%28VS.85%29.aspx"> 071 * http://msdn.microsoft.com/en-us/library/aa366978%28VS.85%29.aspx</A> and the 072 * values of the flags are documented at 073 * <A HREF="http://msdn.microsoft.com/en-us/library/cc223347.aspx"> 074 * http://msdn.microsoft.com/en-us/library/cc223347.aspx</A>. 075 * <BR><BR> 076 * <H2>Example</H2> 077 * The following example demonstrates the process for using the DirSync control 078 * to identify changes to user entries below "dc=example,dc=com": 079 * <PRE> 080 * // Create a search request that will be used to identify all users below 081 * // "dc=example,dc=com". 082 * final SearchRequest searchRequest = new SearchRequest("dc=example,dc=com", 083 * SearchScope.SUB, Filter.createEqualityFilter("objectClass", "User")); 084 * 085 * // Define the components that will be included in the DirSync request 086 * // control. 087 * ASN1OctetString cookie = null; 088 * final int flags = ActiveDirectoryDirSyncControl.FLAG_INCREMENTAL_VALUES | 089 * ActiveDirectoryDirSyncControl.FLAG_OBJECT_SECURITY; 090 * 091 * // Create a loop that will be used to keep polling for changes. 092 * while (keepLooping) 093 * { 094 * // Update the controls that will be used for the search request. 095 * searchRequest.setControls(new ActiveDirectoryDirSyncControl(true, flags, 096 * 50, cookie)); 097 * 098 * // Process the search and get the response control. 099 * final SearchResult searchResult = connection.search(searchRequest); 100 * ActiveDirectoryDirSyncControl dirSyncResponse = 101 * ActiveDirectoryDirSyncControl.get(searchResult); 102 * cookie = dirSyncResponse.getCookie(); 103 * 104 * // Process the search result entries because they represent entries that 105 * // have been created or modified. 106 * for (final SearchResultEntry updatedEntry : 107 * searchResult.getSearchEntries()) 108 * { 109 * // Do something with the entry. 110 * } 111 * 112 * // If the client might want to continue the search even after shutting 113 * // down and starting back up later, then persist the cookie now. 114 * } 115 * </PRE> 116 */ 117@NotMutable() 118@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 119public final class ActiveDirectoryDirSyncControl 120 extends Control 121 implements DecodeableControl 122{ 123 /** 124 * The OID (1.2.840.113556.1.4.841) for the DirSync control. 125 */ 126 @NotNull public static final String DIRSYNC_OID = "1.2.840.113556.1.4.841"; 127 128 129 130 /** 131 * The value of the flag that indicates that the client should only be allowed 132 * to view objects and attributes that are otherwise accessible to the client. 133 */ 134 public static final int FLAG_OBJECT_SECURITY = 0x0000_0001; 135 136 137 138 /** 139 * The value of the flag that indicates the server should return parent 140 * objects before child objects. 141 */ 142 public static final int FLAG_ANCESTORS_FIRST_ORDER = 0x0000_0800; 143 144 145 146 /** 147 * The value of the flag that indicates that the server should not return 148 * private data in search results. 149 */ 150 public static final int FLAG_PUBLIC_DATA_ONLY = 0x0000_2000; 151 152 153 154 /** 155 * The value of the flag that indicates that only changed values of attributes 156 * should be included in search results. 157 */ 158 public static final int FLAG_INCREMENTAL_VALUES = 0x8000_0000; 159 160 161 162 /** 163 * The serial version UID for this serializable class. 164 */ 165 private static final long serialVersionUID = -2871267685237800654L; 166 167 168 169 // A cookie that may be used to resume a previous DirSync search. 170 @Nullable private final ASN1OctetString cookie; 171 172 // The value of the flags that should be used for DirSync operation. 173 private final int flags; 174 175 // The maximum number of attributes to return. 176 private final int maxAttributeCount; 177 178 179 180 /** 181 * Creates a new empty control instance that is intended to be used only for 182 * decoding controls via the {@code DecodeableControl} interface. 183 */ 184 ActiveDirectoryDirSyncControl() 185 { 186 this(true, 0, 0, null); 187 } 188 189 190 191 /** 192 * Creates a new DirSync control with the provided information. 193 * 194 * @param isCritical Indicates whether this control should be marked 195 * critical. 196 * @param flags The value of the flags that should be used for 197 * DirSync operation. This should be zero if no 198 * special flags or needed, or a bitwise OR of the 199 * values of the individual flags that are desired. 200 * @param maxAttributeCount The maximum number of attributes to return. 201 * @param cookie A cookie that may be used to resume a previous 202 * DirSync search. This may be {@code null} if 203 * no previous cookie is available. 204 */ 205 public ActiveDirectoryDirSyncControl(final boolean isCritical, 206 final int flags, 207 final int maxAttributeCount, 208 @Nullable final ASN1OctetString cookie) 209 { 210 super(DIRSYNC_OID, isCritical, 211 encodeValue(flags, maxAttributeCount, cookie)); 212 213 this.flags = flags; 214 this.maxAttributeCount = maxAttributeCount; 215 216 if (cookie == null) 217 { 218 this.cookie = new ASN1OctetString(); 219 } 220 else 221 { 222 this.cookie = cookie; 223 } 224 } 225 226 227 228 /** 229 * Creates a new DirSync control with settings decoded from the provided 230 * control information. 231 * 232 * @param oid The OID of the control to be decoded. 233 * @param isCritical The criticality of the control to be decoded. 234 * @param value The value of the control to be decoded. 235 * 236 * @throws LDAPException If a problem is encountered while attempting to 237 * decode the control value as appropriate for a 238 * DirSync control. 239 */ 240 public ActiveDirectoryDirSyncControl(@NotNull final String oid, 241 final boolean isCritical, 242 @Nullable final ASN1OctetString value) 243 throws LDAPException 244 { 245 super(oid, isCritical, value); 246 247 if (value == null) 248 { 249 throw new LDAPException(ResultCode.DECODING_ERROR, 250 ERR_DIRSYNC_CONTROL_NO_VALUE.get()); 251 } 252 253 try 254 { 255 final ASN1Element[] elements = 256 ASN1Sequence.decodeAsSequence(value.getValue()).elements(); 257 flags = ASN1Integer.decodeAsInteger(elements[0]).intValue(); 258 maxAttributeCount = ASN1Integer.decodeAsInteger(elements[1]).intValue(); 259 cookie = ASN1OctetString.decodeAsOctetString(elements[2]); 260 } 261 catch (final Exception e) 262 { 263 Debug.debugException(e); 264 265 throw new LDAPException(ResultCode.DECODING_ERROR, 266 ERR_DIRSYNC_CONTROL_DECODE_ERROR.get( 267 StaticUtils.getExceptionMessage(e)), 268 e); 269 } 270 } 271 272 273 274 /** 275 * Encodes the provided information into a format appropriate for use as the 276 * value of a DirSync control. 277 * 278 * @param flags The value of the flags that should be used for 279 * DirSync operation. This should be zero if no 280 * special flags or needed, or a bitwise OR of the 281 * values of the individual flags that are desired. 282 * @param maxAttributeCount The maximum number of attributes to return. 283 * @param cookie A cookie that may be used to resume a previous 284 * DirSync search. This may be {@code null} if 285 * no previous cookie is available. 286 * 287 * @return An ASN.1 octet string containing the encoded control value. 288 */ 289 @NotNull() 290 private static ASN1OctetString encodeValue(final int flags, 291 final int maxAttributeCount, 292 @Nullable final ASN1OctetString cookie) 293 { 294 final ASN1Element[] valueElements = new ASN1Element[3]; 295 valueElements[0] = new ASN1Integer(flags); 296 valueElements[1] = new ASN1Integer(maxAttributeCount); 297 298 if (cookie == null) 299 { 300 valueElements[2] = new ASN1OctetString(); 301 } 302 else 303 { 304 valueElements[2] = cookie; 305 } 306 307 return new ASN1OctetString(new ASN1Sequence(valueElements).encode()); 308 } 309 310 311 312 /** 313 * {@inheritDoc} 314 */ 315 @Override() 316 @NotNull() 317 public ActiveDirectoryDirSyncControl decodeControl(@NotNull final String oid, 318 final boolean isCritical, @Nullable final ASN1OctetString value) 319 throws LDAPException 320 { 321 return new ActiveDirectoryDirSyncControl(oid, isCritical, value); 322 } 323 324 325 326 /** 327 * Retrieves the value of the flags that should be used for DirSync operation. 328 * 329 * @return The value of the flags that should be used for DirSync operation. 330 */ 331 public int getFlags() 332 { 333 return flags; 334 } 335 336 337 338 /** 339 * Retrieves the maximum number of attributes to return. 340 * 341 * @return The maximum number of attributes to return. 342 */ 343 public int getMaxAttributeCount() 344 { 345 return maxAttributeCount; 346 } 347 348 349 350 /** 351 * Retrieves a cookie that may be used to resume a previous DirSync search, 352 * if available. 353 * 354 * @return A cookie that may be used to resume a previous DirSync search, or 355 * a zero-length cookie if there is none. 356 */ 357 @Nullable() 358 public ASN1OctetString getCookie() 359 { 360 return cookie; 361 } 362 363 364 365 /** 366 * Extracts a DirSync response control from the provided result. 367 * 368 * @param result The result from which to retrieve the DirSync response 369 * control. 370 * 371 * @return The DirSync response control contained in the provided result, or 372 * {@code null} if the result did not include a DirSync response 373 * control. 374 * 375 * @throws LDAPException If a problem is encountered while attempting to 376 * decode the DirSync response control contained in 377 * the provided result. 378 */ 379 @Nullable() 380 public static ActiveDirectoryDirSyncControl get( 381 @NotNull final SearchResult result) 382 throws LDAPException 383 { 384 final Control c = result.getResponseControl(DIRSYNC_OID); 385 if (c == null) 386 { 387 return null; 388 } 389 390 if (c instanceof ActiveDirectoryDirSyncControl) 391 { 392 return (ActiveDirectoryDirSyncControl) c; 393 } 394 else 395 { 396 return new ActiveDirectoryDirSyncControl(c.getOID(), c.isCritical(), 397 c.getValue()); 398 } 399 } 400 401 402 403 /** 404 * {@inheritDoc} 405 */ 406 @Override() 407 @NotNull() 408 public String getControlName() 409 { 410 return INFO_CONTROL_NAME_DIRSYNC.get(); 411 } 412 413 414 415 /** 416 * {@inheritDoc} 417 */ 418 @Override() 419 public void toString(@NotNull final StringBuilder buffer) 420 { 421 buffer.append("ActiveDirectoryDirSyncControl(isCritical="); 422 buffer.append(isCritical()); 423 buffer.append(", flags="); 424 buffer.append(flags); 425 buffer.append(", maxAttributeCount="); 426 buffer.append(maxAttributeCount); 427 buffer.append(", cookie=byte["); 428 buffer.append(cookie.getValueLength()); 429 buffer.append("])"); 430 } 431}