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