001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * Copyright 2012-2014 ForgeRock AS 024 * 025 */ 026 027package org.forgerock.opendj.examples; 028 029import java.io.IOException; 030import java.util.Collection; 031 032import org.forgerock.opendj.ldap.ByteString; 033import org.forgerock.opendj.ldap.Connection; 034import org.forgerock.opendj.ldap.DecodeException; 035import org.forgerock.opendj.ldap.DecodeOptions; 036import org.forgerock.opendj.ldap.Entry; 037import org.forgerock.opendj.ldap.LdapException; 038import org.forgerock.opendj.ldap.Filter; 039import org.forgerock.opendj.ldap.LDAPConnectionFactory; 040import org.forgerock.opendj.ldap.ModificationType; 041import org.forgerock.opendj.ldap.ResultCode; 042import org.forgerock.opendj.ldap.RootDSE; 043import org.forgerock.opendj.ldap.SearchResultHandler; 044import org.forgerock.opendj.ldap.SearchResultReferenceIOException; 045import org.forgerock.opendj.ldap.SearchScope; 046import org.forgerock.opendj.ldap.SortKey; 047import org.forgerock.opendj.ldap.controls.ADNotificationRequestControl; 048import org.forgerock.opendj.ldap.controls.AssertionRequestControl; 049import org.forgerock.opendj.ldap.controls.AuthorizationIdentityRequestControl; 050import org.forgerock.opendj.ldap.controls.AuthorizationIdentityResponseControl; 051import org.forgerock.opendj.ldap.controls.EntryChangeNotificationResponseControl; 052import org.forgerock.opendj.ldap.controls.GetEffectiveRightsRequestControl; 053import org.forgerock.opendj.ldap.controls.ManageDsaITRequestControl; 054import org.forgerock.opendj.ldap.controls.MatchedValuesRequestControl; 055import org.forgerock.opendj.ldap.controls.PasswordExpiredResponseControl; 056import org.forgerock.opendj.ldap.controls.PasswordExpiringResponseControl; 057import org.forgerock.opendj.ldap.controls.PasswordPolicyRequestControl; 058import org.forgerock.opendj.ldap.controls.PasswordPolicyResponseControl; 059import org.forgerock.opendj.ldap.controls.PermissiveModifyRequestControl; 060import org.forgerock.opendj.ldap.controls.PersistentSearchChangeType; 061import org.forgerock.opendj.ldap.controls.PersistentSearchRequestControl; 062import org.forgerock.opendj.ldap.controls.PostReadRequestControl; 063import org.forgerock.opendj.ldap.controls.PostReadResponseControl; 064import org.forgerock.opendj.ldap.controls.PreReadRequestControl; 065import org.forgerock.opendj.ldap.controls.PreReadResponseControl; 066import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl; 067import org.forgerock.opendj.ldap.controls.ServerSideSortRequestControl; 068import org.forgerock.opendj.ldap.controls.ServerSideSortResponseControl; 069import org.forgerock.opendj.ldap.controls.SimplePagedResultsControl; 070import org.forgerock.opendj.ldap.controls.SubentriesRequestControl; 071import org.forgerock.opendj.ldap.controls.SubtreeDeleteRequestControl; 072import org.forgerock.opendj.ldap.controls.VirtualListViewRequestControl; 073import org.forgerock.opendj.ldap.controls.VirtualListViewResponseControl; 074import org.forgerock.opendj.ldap.requests.BindRequest; 075import org.forgerock.opendj.ldap.requests.DeleteRequest; 076import org.forgerock.opendj.ldap.requests.ModifyRequest; 077import org.forgerock.opendj.ldap.requests.Requests; 078import org.forgerock.opendj.ldap.requests.SearchRequest; 079import org.forgerock.opendj.ldap.responses.BindResult; 080import org.forgerock.opendj.ldap.responses.Result; 081import org.forgerock.opendj.ldap.responses.SearchResultEntry; 082import org.forgerock.opendj.ldap.responses.SearchResultReference; 083import org.forgerock.opendj.ldif.ConnectionEntryReader; 084import org.forgerock.opendj.ldif.LDIFEntryWriter; 085 086/** 087 * This command-line client demonstrates use of LDAP controls. The client takes 088 * as arguments the host and port for the directory server, and expects to find 089 * the entries and access control instructions as defined in <a 090 * href="http://opendj.forgerock.org/Example.ldif">Example.ldif</a>. 091 * 092 * This client connects as <code>cn=Directory Manager</code> with password 093 * <code>password</code>. Not a best practice; in real code use application 094 * specific credentials to connect, and ensure that your application has access 095 * to use the LDAP controls needed. 096 */ 097public final class Controls { 098 099 /** 100 * Connect to the server, and then try to use some LDAP controls. 101 * 102 * @param args 103 * The command line arguments: host, port 104 */ 105 public static void main(final String[] args) { 106 if (args.length != 2) { 107 System.err.println("Usage: host port"); 108 System.err.println("For example: localhost 1389"); 109 System.exit(1); 110 } 111 final String host = args[0]; 112 final int port = Integer.parseInt(args[1]); 113 114 final LDAPConnectionFactory factory = new LDAPConnectionFactory(host, port); 115 Connection connection = null; 116 try { 117 connection = factory.getConnection(); 118 checkSupportedControls(connection); 119 120 final String user = "cn=Directory Manager"; 121 final char[] password = "password".toCharArray(); 122 connection.bind(user, password); 123 124 // Uncomment a method to run one of the examples. 125 126 //useADNotificationRequestControl(connection); 127 //useAssertionControl(connection); 128 useAuthorizationIdentityRequestControl(connection); 129 // For the EntryChangeNotificationResponseControl see 130 // usePersistentSearchRequestControl() 131 //useGetEffectiveRightsRequestControl(connection); 132 //useManageDsaITRequestControl(connection); 133 //useMatchedValuesRequestControl(connection); 134 //usePasswordExpiredResponseControl(connection); 135 //usePasswordExpiringResponseControl(connection); 136 //usePasswordPolicyRequestControl(connection); 137 //usePermissiveModifyRequestControl(connection); 138 //usePersistentSearchRequestControl(connection); 139 //usePostReadRequestControl(connection); 140 //usePreReadRequestControl(connection); 141 //useProxiedAuthV2RequestControl(connection); 142 //useServerSideSortRequestControl(connection); 143 //useSimplePagedResultsControl(connection); 144 //useSubentriesRequestControl(connection); 145 //useSubtreeDeleteRequestControl(connection); 146 //useVirtualListViewRequestControl(connection); 147 148 } catch (final LdapException e) { 149 System.err.println(e.getMessage()); 150 System.exit(e.getResult().getResultCode().intValue()); 151 return; 152 } finally { 153 if (connection != null) { 154 connection.close(); 155 } 156 } 157 } 158 159 /** 160 * Use the <a 161 * href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms676877(v=vs.85).aspx" 162 * >Microsoft LDAP Notification control</a> 163 * to register a change notification request for a search 164 * on Microsoft Active Directory. 165 * <p/> 166 * This client binds to Active Directory as 167 * {@code cn=Administrator,cn=users,dc=example,dc=com} 168 * with password {@code password}, 169 * and expects entries under {@code dc=example,dc=com}. 170 * 171 * @param connection Active connection to Active Directory server. 172 * @throws LdapException Operation failed. 173 */ 174 static void useADNotificationRequestControl(Connection connection) throws LdapException { 175 176 // --- JCite ADNotification --- 177 final String user = "cn=Administrator,cn=users,dc=example,dc=com"; 178 final char[] password = "password".toCharArray(); 179 connection.bind(user, password); 180 181 final String[] attributes = {"cn", 182 ADNotificationRequestControl.IS_DELETED_ATTR, 183 ADNotificationRequestControl.WHEN_CHANGED_ATTR, 184 ADNotificationRequestControl.WHEN_CREATED_ATTR}; 185 186 SearchRequest request = 187 Requests.newSearchRequest("dc=example,dc=com", 188 SearchScope.WHOLE_SUBTREE, "(objectclass=*)", attributes) 189 .addControl(ADNotificationRequestControl.newControl(true)); 190 191 ConnectionEntryReader reader = connection.search(request); 192 193 try { 194 while (reader.hasNext()) { 195 if (!reader.isReference()) { 196 SearchResultEntry entry = reader.readEntry(); // Updated entry 197 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 198 199 Boolean isDeleted = entry.parseAttribute( 200 ADNotificationRequestControl.IS_DELETED_ATTR 201 ).asBoolean(); 202 if (isDeleted != null && isDeleted) { 203 // Handle entry deletion 204 writer.writeComment("Deleted entry: " + entry.getName()); 205 writer.writeEntry(entry); 206 writer.flush(); 207 } 208 String whenCreated = entry.parseAttribute( 209 ADNotificationRequestControl.WHEN_CREATED_ATTR) 210 .asString(); 211 String whenChanged = entry.parseAttribute( 212 ADNotificationRequestControl.WHEN_CHANGED_ATTR) 213 .asString(); 214 if (whenCreated != null && whenChanged != null) { 215 if (whenCreated.equals(whenChanged)) { 216 // Handle entry addition 217 writer.writeComment("Added entry: " + entry.getName()); 218 writer.writeEntry(entry); 219 writer.flush(); 220 } else { 221 // Handle entry modification 222 writer.writeComment("Modified entry: " + entry.getName()); 223 writer.writeEntry(entry); 224 writer.flush(); 225 } 226 } 227 } else { 228 reader.readReference(); // Read and ignore reference 229 } 230 } 231 } catch (final LdapException e) { 232 System.err.println(e.getMessage()); 233 System.exit(e.getResult().getResultCode().intValue()); 234 } catch (final SearchResultReferenceIOException e) { 235 System.err.println("Got search reference(s): " + e.getReference().getURIs()); 236 } catch (final IOException e) { 237 System.err.println(e.getMessage()); 238 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 239 } 240 // --- JCite ADNotification --- 241 } 242 243 /** 244 * Use the LDAP assertion control to modify Babs Jensen's description if 245 * her entry does not have a description, yet. 246 * 247 * @param connection 248 * Active connection to LDAP server containing <a 249 * href="http://opendj.forgerock.org/Example.ldif" 250 * >Example.ldif</a> content. 251 * @throws LdapException 252 * Operation failed. 253 */ 254 static void useAssertionControl(Connection connection) throws LdapException { 255 // --- JCite assertion --- 256 if (isSupported(AssertionRequestControl.OID)) { 257 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 258 259 final ModifyRequest request = 260 Requests.newModifyRequest(dn) 261 .addControl(AssertionRequestControl.newControl( 262 true, Filter.valueOf("!(description=*)"))) 263 .addModification(ModificationType.ADD, "description", 264 "Created using LDAP assertion control"); 265 266 connection.modify(request); 267 268 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 269 try { 270 writer.writeEntry(connection.readEntry(dn, "description")); 271 writer.close(); 272 } catch (final IOException e) { 273 System.err.println(e.getMessage()); 274 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 275 } 276 } else { 277 System.err.println("AssertionRequestControl not supported."); 278 } 279 // --- JCite assertion --- 280 } 281 282 /** 283 * Use the LDAP Authorization Identity Controls to get the authorization ID. 284 * 285 * @param connection 286 * Active connection to LDAP server containing <a 287 * href="http://opendj.forgerock.org/Example.ldif" 288 * >Example.ldif</a> content. 289 * @throws LdapException 290 * Operation failed. 291 */ 292 static void useAuthorizationIdentityRequestControl(Connection connection) throws LdapException { 293 // --- JCite authzid --- 294 if (isSupported(AuthorizationIdentityRequestControl.OID)) { 295 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 296 final char[] pwd = "hifalutin".toCharArray(); 297 298 System.out.println("Binding as " + dn); 299 final BindRequest request = 300 Requests.newSimpleBindRequest(dn, pwd) 301 .addControl(AuthorizationIdentityRequestControl.newControl(true)); 302 303 final BindResult result = connection.bind(request); 304 try { 305 final AuthorizationIdentityResponseControl control = 306 result.getControl(AuthorizationIdentityResponseControl.DECODER, 307 new DecodeOptions()); 308 System.out.println("Authorization ID returned: " 309 + control.getAuthorizationID()); 310 } catch (final DecodeException e) { 311 System.err.println(e.getMessage()); 312 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 313 } 314 } else { 315 System.err.println("AuthorizationIdentityRequestControl not supported."); 316 } 317 // --- JCite authzid --- 318 } 319 320 /** 321 * Use the GetEffectiveRights Request Control to determine what sort of 322 * access a user has to particular attributes on an entry. 323 * 324 * @param connection 325 * Active connection to LDAP server containing <a 326 * href="http://opendj.forgerock.org/Example.ldif" 327 * >Example.ldif</a> content. 328 * @throws LdapException 329 * Operation failed. 330 */ 331 static void useGetEffectiveRightsRequestControl(Connection connection) throws LdapException { 332 // --- JCite effective rights --- 333 if (isSupported(GetEffectiveRightsRequestControl.OID)) { 334 final String authDN = "uid=kvaughan,ou=People,dc=example,dc=com"; 335 336 final SearchRequest request = 337 Requests.newSearchRequest( 338 "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, 339 "(uid=bjensen)", "cn", "aclRights", "aclRightsInfo") 340 .addControl(GetEffectiveRightsRequestControl.newControl( 341 true, authDN, "cn")); 342 343 final ConnectionEntryReader reader = connection.search(request); 344 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 345 try { 346 while (reader.hasNext()) { 347 if (!reader.isReference()) { 348 final SearchResultEntry entry = reader.readEntry(); 349 writer.writeEntry(entry); 350 } 351 } 352 writer.close(); 353 } catch (final LdapException e) { 354 System.err.println(e.getMessage()); 355 System.exit(e.getResult().getResultCode().intValue()); 356 } catch (final SearchResultReferenceIOException e) { 357 System.err.println("Got search reference(s): " + e.getReference().getURIs()); 358 } catch (final IOException e) { 359 System.err.println(e.getMessage()); 360 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 361 } 362 } else { 363 System.err.println("GetEffectiveRightsRequestControl not supported."); 364 } 365 // --- JCite effective rights --- 366 } 367 368 /** 369 * Use the ManageDsaIT Request Control to show the difference between a 370 * referral accessed with and without use of the control. 371 * 372 * @param connection 373 * Active connection to LDAP server containing <a 374 * href="http://opendj.forgerock.org/Example.ldif" 375 * >Example.ldif</a> content. 376 * @throws LdapException 377 * Operation failed. 378 */ 379 static void useManageDsaITRequestControl(Connection connection) throws LdapException { 380 // --- JCite manage DsaIT --- 381 if (isSupported(ManageDsaITRequestControl.OID)) { 382 final String dn = "dc=ref,dc=com"; 383 384 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 385 try { 386 System.out.println("Referral without the ManageDsaIT control."); 387 SearchRequest request = Requests.newSearchRequest(dn, 388 SearchScope.SUBORDINATES, "(objectclass=*)", ""); 389 final ConnectionEntryReader reader = connection.search(request); 390 while (reader.hasNext()) { 391 if (reader.isReference()) { 392 final SearchResultReference ref = reader.readReference(); 393 System.out.println("Reference: " + ref.getURIs()); 394 } 395 } 396 397 System.out.println("Referral with the ManageDsaIT control."); 398 request.addControl(ManageDsaITRequestControl.newControl(true)); 399 final SearchResultEntry entry = connection.searchSingleEntry(request); 400 writer.writeEntry(entry); 401 writer.close(); 402 } catch (final LdapException e) { 403 System.err.println(e.getMessage()); 404 System.exit(e.getResult().getResultCode().intValue()); 405 } catch (final SearchResultReferenceIOException e) { 406 System.err.println("Got search reference(s): " + e.getReference().getURIs()); 407 } catch (final IOException e) { 408 System.err.println(e.getMessage()); 409 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 410 } 411 } else { 412 System.err.println("ManageDsaITRequestControl not supported."); 413 } 414 // --- JCite manage DsaIT --- 415 } 416 417 /** 418 * Use the Matched Values Request Control to show read only one attribute 419 * value. 420 * 421 * @param connection 422 * Active connection to LDAP server containing <a 423 * href="http://opendj.forgerock.org/Example.ldif" 424 * >Example.ldif</a> content. 425 * @throws LdapException 426 * Operation failed. 427 */ 428 static void useMatchedValuesRequestControl(Connection connection) throws LdapException { 429 // --- JCite matched values --- 430 if (isSupported(MatchedValuesRequestControl.OID)) { 431 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 432 final SearchRequest request = 433 Requests.newSearchRequest(dn, SearchScope.BASE_OBJECT, 434 "(objectclass=*)", "cn") 435 .addControl(MatchedValuesRequestControl.newControl( 436 true, "(cn=Babs Jensen)")); 437 438 final SearchResultEntry entry = connection.searchSingleEntry(request); 439 System.out.println("Reading entry with matched values request."); 440 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 441 try { 442 writer.writeEntry(entry); 443 writer.close(); 444 } catch (final IOException e) { 445 System.err.println(e.getMessage()); 446 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 447 } 448 } else { 449 System.err.println("MatchedValuesRequestControl not supported."); 450 } 451 // --- JCite matched values --- 452 } 453 454 /** 455 * Check the Password Expired Response Control. To get this code to output 456 * something, you must first set up an appropriate password policy and wait 457 * for Barbara Jensen's password to expire. 458 * 459 * @param connection 460 * Active connection to LDAP server containing <a 461 * href="http://opendj.forgerock.org/Example.ldif" 462 * >Example.ldif</a> content. 463 */ 464 static void usePasswordExpiredResponseControl(Connection connection) { 465 // --- JCite password expired --- 466 if (isSupported(PasswordExpiredResponseControl.OID)) { 467 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 468 final char[] pwd = "hifalutin".toCharArray(); 469 470 try { 471 connection.bind(dn, pwd); 472 } catch (final LdapException e) { 473 final Result result = e.getResult(); 474 try { 475 final PasswordExpiredResponseControl control = 476 result.getControl(PasswordExpiredResponseControl.DECODER, 477 new DecodeOptions()); 478 if (control != null && control.hasValue()) { 479 System.out.println("Password expired for " + dn); 480 } 481 } catch (final DecodeException de) { 482 System.err.println(de.getMessage()); 483 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 484 } 485 } 486 } else { 487 System.err.println("PasswordExpiredResponseControl not supported."); 488 } 489 // --- JCite password expired --- 490 } 491 492 /** 493 * Check the Password Expiring Response Control. To get this code to output 494 * something, you must first set up an appropriate password policy and wait 495 * for Barbara Jensen's password to get old enough that the server starts 496 * warning about expiration. 497 * 498 * @param connection 499 * Active connection to LDAP server containing <a 500 * href="http://opendj.forgerock.org/Example.ldif" 501 * >Example.ldif</a> content. 502 * @throws LdapException 503 * Operation failed. 504 */ 505 static void usePasswordExpiringResponseControl(Connection connection) throws LdapException { 506 // --- JCite password expiring --- 507 if (isSupported(PasswordExpiringResponseControl.OID)) { 508 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 509 final char[] pwd = "hifalutin".toCharArray(); 510 511 final BindResult result = connection.bind(dn, pwd); 512 try { 513 final PasswordExpiringResponseControl control = 514 result.getControl(PasswordExpiringResponseControl.DECODER, 515 new DecodeOptions()); 516 if (control != null && control.hasValue()) { 517 System.out.println("Password for " + dn + " expires in " 518 + control.getSecondsUntilExpiration() + " seconds."); 519 } 520 } catch (final DecodeException de) { 521 System.err.println(de.getMessage()); 522 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 523 } 524 } else { 525 System.err.println("PasswordExpiringResponseControl not supported"); 526 } 527 // --- JCite password expiring --- 528 } 529 530 /** 531 * Use the Password Policy Request and Response Controls. To get this code 532 * to output something, you must first set up an appropriate password policy 533 * and wait for Barbara Jensen's password to get old enough that the server 534 * starts warning about expiration, or for the password to expire. 535 * 536 * @param connection 537 * Active connection to LDAP server containing <a 538 * href="http://opendj.forgerock.org/Example.ldif" 539 * >Example.ldif</a> content. 540 */ 541 static void usePasswordPolicyRequestControl(Connection connection) { 542 // --- JCite password policy --- 543 if (isSupported(PasswordPolicyRequestControl.OID)) { 544 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 545 final char[] pwd = "hifalutin".toCharArray(); 546 547 try { 548 final BindRequest request = Requests.newSimpleBindRequest(dn, pwd) 549 .addControl(PasswordPolicyRequestControl.newControl(true)); 550 551 final BindResult result = connection.bind(request); 552 553 final PasswordPolicyResponseControl control = 554 result.getControl(PasswordPolicyResponseControl.DECODER, 555 new DecodeOptions()); 556 if (control != null && control.getWarningType() != null) { 557 System.out.println("Password policy warning " 558 + control.getWarningType() + ", value " 559 + control.getWarningValue() + " for " + dn); 560 } 561 } catch (final LdapException e) { 562 final Result result = e.getResult(); 563 try { 564 final PasswordPolicyResponseControl control = 565 result.getControl(PasswordPolicyResponseControl.DECODER, 566 new DecodeOptions()); 567 if (control != null) { 568 System.out.println("Password policy error " 569 + control.getErrorType() + " for " + dn); 570 } 571 } catch (final DecodeException de) { 572 System.err.println(de.getMessage()); 573 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 574 } 575 } catch (final DecodeException e) { 576 System.err.println(e.getMessage()); 577 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 578 } 579 } else { 580 System.err.println("PasswordPolicyRequestControl not supported"); 581 } 582 // --- JCite password policy --- 583 } 584 585 /** 586 * Use Permissive Modify Request Control to try to add an attribute that 587 * already exists. 588 * 589 * @param connection 590 * Active connection to LDAP server containing <a 591 * href="http://opendj.forgerock.org/Example.ldif" 592 * >Example.ldif</a> content. 593 * @throws LdapException 594 * Operation failed. 595 */ 596 static void usePermissiveModifyRequestControl(Connection connection) throws LdapException { 597 // --- JCite permissive modify --- 598 if (isSupported(PermissiveModifyRequestControl.OID)) { 599 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 600 601 final ModifyRequest request = 602 Requests.newModifyRequest(dn) 603 .addControl(PermissiveModifyRequestControl.newControl(true)) 604 .addModification(ModificationType.ADD, "uid", "bjensen"); 605 606 connection.modify(request); 607 System.out.println("Permissive modify did not complain about " 608 + "attempt to add uid: bjensen to " + dn + "."); 609 } else { 610 System.err.println("PermissiveModifyRequestControl not supported"); 611 } 612 // --- JCite permissive modify --- 613 } 614 615 /** 616 * Use the LDAP PersistentSearchRequestControl to set up a persistent 617 * search. Also use the Entry Change Notification Response Control to get 618 * details about why an entry was returned for a persistent search. 619 * 620 * After you set this up, use another application to make changes to user 621 * entries under dc=example,dc=com. 622 * 623 * @param connection 624 * Active connection to LDAP server containing <a 625 * href="http://opendj.forgerock.org/Example.ldif" 626 * >Example.ldif</a> content. 627 * @throws LdapException 628 * Operation failed. 629 */ 630 static void usePersistentSearchRequestControl(Connection connection) throws LdapException { 631 // --- JCite psearch --- 632 if (isSupported(PersistentSearchRequestControl.OID)) { 633 final SearchRequest request = 634 Requests.newSearchRequest( 635 "dc=example,dc=com", SearchScope.WHOLE_SUBTREE, 636 "(objectclass=inetOrgPerson)", "cn") 637 .addControl(PersistentSearchRequestControl.newControl( 638 true, true, true, // isCritical, changesOnly, returnECs 639 PersistentSearchChangeType.ADD, 640 PersistentSearchChangeType.DELETE, 641 PersistentSearchChangeType.MODIFY, 642 PersistentSearchChangeType.MODIFY_DN)); 643 644 final ConnectionEntryReader reader = connection.search(request); 645 646 try { 647 while (reader.hasNext()) { 648 if (!reader.isReference()) { 649 final SearchResultEntry entry = reader.readEntry(); 650 System.out.println("Entry changed: " + entry.getName()); 651 652 final EntryChangeNotificationResponseControl control = 653 entry.getControl( 654 EntryChangeNotificationResponseControl.DECODER, 655 new DecodeOptions()); 656 657 final PersistentSearchChangeType type = control.getChangeType(); 658 System.out.println("Change type: " + type); 659 if (type.equals(PersistentSearchChangeType.MODIFY_DN)) { 660 System.out.println("Previous DN: " + control.getPreviousName()); 661 } 662 System.out.println("Change number: " + control.getChangeNumber()); 663 System.out.println(); // Add a blank line. 664 } 665 } 666 } catch (final DecodeException e) { 667 System.err.println(e.getMessage()); 668 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 669 } catch (final LdapException e) { 670 System.err.println(e.getMessage()); 671 System.exit(e.getResult().getResultCode().intValue()); 672 } catch (final SearchResultReferenceIOException e) { 673 System.err.println("Got search reference(s): " + e.getReference().getURIs()); 674 } 675 } else { 676 System.err.println("PersistentSearchRequestControl not supported."); 677 } 678 // --- JCite psearch --- 679 } 680 681 682 /** 683 * Use Post Read Controls to get entry content after a modification. 684 * 685 * @param connection 686 * Active connection to LDAP server containing <a 687 * href="http://opendj.forgerock.org/Example.ldif" 688 * >Example.ldif</a> content. 689 * @throws LdapException 690 * Operation failed. 691 */ 692 static void usePostReadRequestControl(Connection connection) throws LdapException { 693 // --- JCite post read --- 694 if (isSupported(PostReadRequestControl.OID)) { 695 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 696 697 final ModifyRequest request = 698 Requests.newModifyRequest(dn) 699 .addControl(PostReadRequestControl.newControl(true, "description")) 700 .addModification(ModificationType.REPLACE, 701 "description", "Using the PostReadRequestControl"); 702 703 final Result result = connection.modify(request); 704 try { 705 final PostReadResponseControl control = 706 result.getControl(PostReadResponseControl.DECODER, 707 new DecodeOptions()); 708 final Entry entry = control.getEntry(); 709 710 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 711 writer.writeEntry(entry); 712 writer.close(); 713 } catch (final DecodeException e) { 714 System.err.println(e.getMessage()); 715 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 716 } catch (final IOException e) { 717 System.err.println(e.getMessage()); 718 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 719 } 720 } else { 721 System.err.println("PostReadRequestControl not supported"); 722 } 723 // --- JCite post read --- 724 } 725 726 /** 727 * Use Pre Read Controls to get entry content before a modification. 728 * 729 * @param connection 730 * Active connection to LDAP server containing <a 731 * href="http://opendj.forgerock.org/Example.ldif" 732 * >Example.ldif</a> content. 733 * @throws LdapException 734 * Operation failed. 735 */ 736 static void usePreReadRequestControl(Connection connection) throws LdapException { 737 // --- JCite pre read --- 738 if (isSupported(PreReadRequestControl.OID)) { 739 final String dn = "uid=bjensen,ou=People,dc=example,dc=com"; 740 741 final ModifyRequest request = 742 Requests.newModifyRequest(dn) 743 .addControl(PreReadRequestControl.newControl(true, "mail")) 744 .addModification( 745 ModificationType.REPLACE, "mail", "modified@example.com"); 746 747 final Result result = connection.modify(request); 748 try { 749 final PreReadResponseControl control = 750 result.getControl(PreReadResponseControl.DECODER, 751 new DecodeOptions()); 752 final Entry entry = control.getEntry(); 753 754 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 755 writer.writeEntry(entry); 756 writer.close(); 757 } catch (final DecodeException e) { 758 System.err.println(e.getMessage()); 759 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 760 } catch (final IOException e) { 761 System.err.println(e.getMessage()); 762 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 763 } 764 } else { 765 System.err.println("PreReadRequestControl not supported"); 766 } 767 // --- JCite pre read --- 768 } 769 770 /** 771 * Use proxied authorization to modify an identity as another user. 772 * 773 * @param connection 774 * Active connection to LDAP server containing <a 775 * href="http://opendj.forgerock.org/Example.ldif" 776 * >Example.ldif</a> content. 777 * @throws LdapException 778 * Operation failed. 779 */ 780 static void useProxiedAuthV2RequestControl(Connection connection) throws LdapException { 781 // --- JCite proxied authzv2 --- 782 if (isSupported(ProxiedAuthV2RequestControl.OID)) { 783 final String bindDN = "cn=My App,ou=Apps,dc=example,dc=com"; 784 final String targetDn = "uid=bjensen,ou=People,dc=example,dc=com"; 785 final String authzId = "dn:uid=kvaughan,ou=People,dc=example,dc=com"; 786 787 final ModifyRequest request = 788 Requests.newModifyRequest(targetDn) 789 .addControl(ProxiedAuthV2RequestControl.newControl(authzId)) 790 .addModification(ModificationType.REPLACE, "description", 791 "Done with proxied authz"); 792 793 connection.bind(bindDN, "password".toCharArray()); 794 connection.modify(request); 795 final Entry entry = connection.readEntry(targetDn, "description"); 796 797 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 798 try { 799 writer.writeEntry(entry); 800 writer.close(); 801 } catch (final IOException e) { 802 System.err.println(e.getMessage()); 803 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 804 } 805 } else { 806 System.err.println("ProxiedAuthV2RequestControl not supported"); 807 } 808 // --- JCite proxied authzv2 --- 809 } 810 811 /** 812 * Use the server-side sort controls. 813 * 814 * @param connection 815 * Active connection to LDAP server containing <a 816 * href="http://opendj.forgerock.org/Example.ldif" 817 * >Example.ldif</a> content. 818 * @throws LdapException 819 * Operation failed. 820 */ 821 // --- JCite server-side sort --- 822 static void useServerSideSortRequestControl(Connection connection) throws LdapException { 823 if (isSupported(ServerSideSortRequestControl.OID)) { 824 final SearchRequest request = 825 Requests.newSearchRequest("ou=People,dc=example,dc=com", 826 SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") 827 .addControl(ServerSideSortRequestControl.newControl( 828 true, new SortKey("cn"))); 829 830 final SearchResultHandler resultHandler = new MySearchResultHandler(); 831 final Result result = connection.search(request, resultHandler); 832 833 try { 834 final ServerSideSortResponseControl control = 835 result.getControl(ServerSideSortResponseControl.DECODER, 836 new DecodeOptions()); 837 if (control != null && control.getResult() == ResultCode.SUCCESS) { 838 System.out.println("# Entries are sorted."); 839 } else { 840 System.out.println("# Entries not necessarily sorted"); 841 } 842 } catch (final DecodeException e) { 843 System.err.println(e.getMessage()); 844 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 845 } 846 } else { 847 System.err.println("ServerSideSortRequestControl not supported"); 848 } 849 } 850 851 private static class MySearchResultHandler implements SearchResultHandler { 852 853 @Override 854 public boolean handleEntry(SearchResultEntry entry) { 855 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 856 try { 857 writer.writeEntry(entry); 858 writer.flush(); 859 } catch (final IOException e) { 860 System.err.println(e.getMessage()); 861 return false; 862 } 863 return true; 864 } 865 866 @Override 867 public boolean handleReference(SearchResultReference reference) { 868 System.out.println("Got a reference: " + reference); 869 return false; 870 } 871 } 872 // --- JCite server-side sort --- 873 874 /** 875 * Use the simple paged results mechanism. 876 * 877 * @param connection 878 * Active connection to LDAP server containing <a 879 * href="http://opendj.forgerock.org/Example.ldif" 880 * >Example.ldif</a> content. 881 * @throws LdapException 882 * Operation failed. 883 */ 884 static void useSimplePagedResultsControl(Connection connection) throws LdapException { 885 // --- JCite simple paged results --- 886 if (isSupported(SimplePagedResultsControl.OID)) { 887 ByteString cookie = ByteString.empty(); 888 SearchRequest request; 889 final SearchResultHandler resultHandler = new MySearchResultHandler(); 890 Result result; 891 892 int page = 1; 893 do { 894 System.out.println("# Simple paged results: Page " + page); 895 896 request = 897 Requests.newSearchRequest("dc=example,dc=com", 898 SearchScope.WHOLE_SUBTREE, "(sn=Jensen)", "cn") 899 .addControl(SimplePagedResultsControl.newControl( 900 true, 3, cookie)); 901 902 result = connection.search(request, resultHandler); 903 try { 904 SimplePagedResultsControl control = 905 result.getControl(SimplePagedResultsControl.DECODER, 906 new DecodeOptions()); 907 cookie = control.getCookie(); 908 } catch (final DecodeException e) { 909 System.err.println(e.getMessage()); 910 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 911 } 912 913 ++page; 914 } while (cookie.length() != 0); 915 } else { 916 System.err.println("SimplePagedResultsControl not supported"); 917 } 918 // --- JCite simple paged results --- 919 } 920 921 /** 922 * Use the subentries request control. 923 * 924 * @param connection 925 * Active connection to LDAP server containing <a 926 * href="http://opendj.forgerock.org/Example.ldif" 927 * >Example.ldif</a> content. 928 * @throws LdapException 929 * Operation failed. 930 */ 931 static void useSubentriesRequestControl(Connection connection) throws LdapException { 932 // --- JCite subentries --- 933 if (isSupported(SubentriesRequestControl.OID)) { 934 final SearchRequest request = 935 Requests.newSearchRequest("dc=example,dc=com", 936 SearchScope.WHOLE_SUBTREE, 937 "cn=*Class of Service", "cn", "subtreeSpecification") 938 .addControl(SubentriesRequestControl.newControl( 939 true, true)); 940 941 final ConnectionEntryReader reader = connection.search(request); 942 final LDIFEntryWriter writer = new LDIFEntryWriter(System.out); 943 try { 944 while (reader.hasNext()) { 945 if (reader.isEntry()) { 946 final SearchResultEntry entry = reader.readEntry(); 947 writer.writeEntry(entry); 948 } 949 } 950 writer.close(); 951 } catch (final LdapException e) { 952 System.err.println(e.getMessage()); 953 System.exit(e.getResult().getResultCode().intValue()); 954 } catch (final SearchResultReferenceIOException e) { 955 System.err.println("Got search reference(s): " + e.getReference().getURIs()); 956 } catch (final IOException e) { 957 System.err.println(e.getMessage()); 958 System.exit(ResultCode.CLIENT_SIDE_LOCAL_ERROR.intValue()); 959 } 960 } else { 961 System.err.println("SubentriesRequestControl not supported"); 962 } 963 // --- JCite subentries --- 964 } 965 966 /** 967 * Use the subtree delete control. 968 * 969 * @param connection 970 * Active connection to LDAP server containing <a 971 * href="http://opendj.forgerock.org/Example.ldif" 972 * >Example.ldif</a> content. 973 * @throws LdapException 974 * Operation failed. 975 */ 976 static void useSubtreeDeleteRequestControl(Connection connection) throws LdapException { 977 // --- JCite tree delete --- 978 if (isSupported(SubtreeDeleteRequestControl.OID)) { 979 980 final String dn = "ou=Apps,dc=example,dc=com"; 981 final DeleteRequest request = 982 Requests.newDeleteRequest(dn) 983 .addControl(SubtreeDeleteRequestControl.newControl(true)); 984 985 final Result result = connection.delete(request); 986 if (result.isSuccess()) { 987 System.out.println("Successfully deleted " + dn 988 + " and all entries below."); 989 } else { 990 System.err.println("Result: " + result.getDiagnosticMessage()); 991 } 992 } else { 993 System.err.println("SubtreeDeleteRequestControl not supported"); 994 } 995 // --- JCite tree delete --- 996 } 997 998 /** 999 * Use the virtual list view controls. In order to set up OpenDJ directory 1000 * server to produce the following output with the example code, use OpenDJ 1001 * Control Panel > Manage Indexes > New VLV Index... to set up a 1002 * virtual list view index for people by last name, using the filter 1003 * {@code (|(givenName=*)(sn=*))}, and sorting first by surname, {@code sn}, 1004 * in ascending order, then by given name also in ascending order 1005 * 1006 * @param connection 1007 * Active connection to LDAP server containing <a 1008 * href="http://opendj.forgerock.org/Example.ldif" 1009 * >Example.ldif</a> content. 1010 * @throws LdapException 1011 * Operation failed. 1012 */ 1013 static void useVirtualListViewRequestControl(Connection connection) throws LdapException { 1014 // --- JCite vlv --- 1015 if (isSupported(VirtualListViewRequestControl.OID)) { 1016 ByteString contextID = ByteString.empty(); 1017 1018 // Add a window of 2 entries on either side of the first sn=Jensen entry. 1019 final SearchRequest request = 1020 Requests.newSearchRequest("ou=People,dc=example,dc=com", 1021 SearchScope.WHOLE_SUBTREE, "(sn=*)", "sn", "givenName") 1022 .addControl(ServerSideSortRequestControl.newControl( 1023 true, new SortKey("sn"))) 1024 .addControl( 1025 VirtualListViewRequestControl.newAssertionControl( 1026 true, 1027 ByteString.valueOf("Jensen"), 1028 2, 2, contextID)); 1029 1030 final SearchResultHandler resultHandler = new MySearchResultHandler(); 1031 final Result result = connection.search(request, resultHandler); 1032 1033 try { 1034 final ServerSideSortResponseControl sssControl = 1035 result.getControl(ServerSideSortResponseControl.DECODER, 1036 new DecodeOptions()); 1037 if (sssControl != null && sssControl.getResult() == ResultCode.SUCCESS) { 1038 System.out.println("# Entries are sorted."); 1039 } else { 1040 System.out.println("# Entries not necessarily sorted"); 1041 } 1042 1043 final VirtualListViewResponseControl vlvControl = 1044 result.getControl(VirtualListViewResponseControl.DECODER, 1045 new DecodeOptions()); 1046 System.out.println("# Position in list: " 1047 + vlvControl.getTargetPosition() + "/" 1048 + vlvControl.getContentCount()); 1049 } catch (final DecodeException e) { 1050 System.err.println(e.getMessage()); 1051 System.exit(ResultCode.CLIENT_SIDE_DECODING_ERROR.intValue()); 1052 } 1053 } else { 1054 System.err.println("VirtualListViewRequestControl not supported"); 1055 } 1056 // --- JCite vlv --- 1057 } 1058 1059 // --- JCite check support --- 1060 /** 1061 * Controls supported by the LDAP server. 1062 */ 1063 private static Collection<String> controls; 1064 1065 /** 1066 * Populate the list of supported LDAP control OIDs. 1067 * 1068 * @param connection 1069 * Active connection to the LDAP server. 1070 * @throws LdapException 1071 * Failed to get list of controls. 1072 */ 1073 static void checkSupportedControls(Connection connection) throws LdapException { 1074 controls = RootDSE.readRootDSE(connection).getSupportedControls(); 1075 } 1076 1077 /** 1078 * Check whether a control is supported. Call {@code checkSupportedControls} 1079 * first. 1080 * 1081 * @param control 1082 * Check support for this control, provided by OID. 1083 * @return True if the control is supported. 1084 */ 1085 static boolean isSupported(final String control) { 1086 return controls != null && controls.contains(control); 1087 } 1088 // --- JCite check support --- 1089 1090 /** 1091 * Constructor not used. 1092 */ 1093 private Controls() { 1094 // Not used. 1095 } 1096}