/** * A delete Record with a control. * * @throws Exception */ @Test public void testWriteDeleteRequestContainingControl() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final DN dn = DN.valueOf("uid=scarter,ou=People,dc=example,dc=com"); // @formatter:off final DeleteRequest changeRequest = Requests.newDeleteRequest(dn) .addControl( PersistentSearchRequestControl.newControl( true, true, true, // isCritical, changesOnly, returnECs PersistentSearchChangeType.ADD, PersistentSearchChangeType.DELETE, PersistentSearchChangeType.MODIFY, PersistentSearchChangeType.MODIFY_DN)); // @formatter:on writer.writeComment("This record contains a control"); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("# This record contains a control"); assertThat(actual.get(1)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.get(2)).contains("control: 2.16.840.1.113730.3.4.3 true"); assertThat(actual.get(3)).isEqualTo("changetype: delete"); assertThat(actual.get(4)).isEqualTo(""); }
/** * Write a modify request. * * @throws Exception */ @Test public void testWriteModifyRequest() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyRequest changeRequest = Requests.newModifyRequest( "version: 1", "dn: cn=scarter,dc=example,dc=com", "changetype: modify", "add: work-phone", "work-phone: 650/506-7000"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); // version number is skipped. assertThat(actual.get(0)).isEqualTo("dn: cn=scarter,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: modify"); assertThat(actual.get(2)).isEqualTo("add: work-phone"); assertThat(actual.get(3)).isEqualTo("work-phone: 650/506-7000"); assertThat(actual.get(4)).isEqualTo("-"); }
/** * Write a modify request using branch exclusion. * * @throws Exception */ @Test public void testWriteModifyRequestBranchExcludedNoMatch() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyRequest changeRequest = (ModifyRequest) Requests.newChangeRecord( "version: 1", "", "dn: cn=scarter,dc=example,dc=com", "changetype: modify", "replace: work-phone", "work-phone: 555-555-1155"); // @formatter:on final DN dn = DN.valueOf("dc=example,dc=org"); writer.setExcludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(6); // all line plus a "" assertThat(actual.get(0)).isEqualTo("dn: cn=scarter,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: modify"); assertThat(actual.get(5)).isEqualTo(""); }
/** * Write a modify request using an exclusion filter attribute. * * @throws Exception */ @Test public void testWriteModifyRequestFilterAttributesExcluded() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyRequest changeRequest = (ModifyRequest) Requests.newChangeRecord( "version: 1", "", "dn: cn=scarter,dc=example,dc=com", "changetype: modify", "replace: work-phone", "work-phone: 555-555-1155"); // @formatter:on writer.setExcludeAttribute(AttributeDescription.valueOf("work-phone")); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(3); assertThat(actual.get(0)).isEqualTo("dn: cn=scarter,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: modify"); assertThat(actual.get(2)).isEqualTo(""); }
private List<DN> findBaseDNs() throws IOException { final List<DN> baseDNs = new LinkedList<DN>(); final SearchRequest request = Requests.newSearchRequest( "cn=backends,cn=config", SearchScope.WHOLE_SUBTREE, "(objectclass=ds-cfg-local-db-backend)", "ds-cfg-base-dn"); FileInputStream is = null; LDIFEntryReader reader = null; try { is = new FileInputStream(installRoot + "/config/config.ldif"); reader = new LDIFEntryReader(is); final EntryReader filteredReader = LDIF.search(reader, request); while (filteredReader.hasNext()) { final Entry entry = filteredReader.readEntry(); final Attribute values = entry.getAttribute("ds-cfg-base-dn"); if (values != null) { for (final ByteString value : values) { baseDNs.add(DN.valueOf(value.toString())); } } } } finally { closeIfNotNull(reader); closeIfNotNull(is); } return baseDNs; }
/** * Write an AddRequestChange LDIF. * * @throws Exception */ @Test public void testWriteAddRequest() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.size()).isEqualTo(14); }
private void doTestRequestTimeout(boolean isPersistentSearch) throws Exception { InetSocketAddress address = TestCaseUtils.findFreeSocketAddress(); /* * Use a mock server implementation which will ignore incoming requests * and leave the client waiting forever for a response. */ @SuppressWarnings("unchecked") LDAPListener listener = new LDAPListener( address, Connections.newServerConnectionFactory(mock(RequestHandler.class))); /* * Use a very long time out in order to prevent the timeout thread from * triggering the timeout. */ LDAPConnectionFactory factory = new LDAPConnectionFactory( address.getHostName(), address.getPort(), Options.defaultOptions() .set(TIMEOUT_IN_MILLISECONDS, TimeUnit.SECONDS.toMillis((long) 100))); GrizzlyLDAPConnection connection = (GrizzlyLDAPConnection) factory.getConnection(); try { SearchRequest request = Requests.newSearchRequest("dc=test", SearchScope.BASE_OBJECT, "(objectClass=*)"); if (isPersistentSearch) { request.addControl(PersistentSearchRequestControl.newControl(true, true, true)); } SearchResultHandler searchHandler = mock(SearchResultHandler.class); @SuppressWarnings("unchecked") ExceptionHandler<LdapException> exceptionHandler = mock(ExceptionHandler.class); connection.searchAsync(request, searchHandler).thenOnException(exceptionHandler); // Pass in a time which is guaranteed to trigger expiration. connection.handleTimeout(System.currentTimeMillis() + 1000000); if (isPersistentSearch) { verifyZeroInteractions(searchHandler); } else { ArgumentCaptor<LdapException> arg = ArgumentCaptor.forClass(LdapException.class); verify(exceptionHandler).handleException(arg.capture()); assertThat(arg.getValue()).isInstanceOf(TimeoutResultException.class); assertThat(arg.getValue().getResult().getResultCode()) .isEqualTo(ResultCode.CLIENT_SIDE_TIMEOUT); } } finally { connection.close(); listener.close(); factory.close(); } }
/** * Test SetIncludeBranch method of LDIFChangeRecordWriter DN included is "dc=example,dc=com", * which is not the one from the record. Record must not be written. * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetIncludeBranchWithNoMatch() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final DN dn = DN.valueOf("dc=example,dc=org"); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setIncludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); // No result expected assertThat(actual.size()).isEqualTo(0); }
/** * Test SetIncludeAttribute method of LDIFChangeRecordWriter. Inserting attribute cn (common name) * & sn (surname) * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetIncludeAttributeWithNoMatch() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setIncludeAttribute(AttributeDescription.valueOf("vip")); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: add"); assertThat(actual.get(2)).contains(""); }
/** * Test SetExcludeBranch method of LDIFChangeRecordWriter. * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetExcludeBranchWithNoMatch() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final DN dn = DN.valueOf("dc=example,dc=com"); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setExcludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); // No values expected - we have excluded the branch. Assert.assertFalse(actual.size() > 0); }
/** * Test SetIncludeBranch method of LDIFChangeRecordWriter verifying right data are present. * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetIncludeBranchWithMatch() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final DN dn = DN.valueOf("dc=example,dc=com"); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setIncludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); // Must contains all the attributes assertThat(actual.get(0)).contains("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.size()).isEqualTo(14); }
/** * Write an ModifyRequest LDIF. * * @throws Exception */ @Test public void testWriteModifyRequestNoModifications() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyRequest changeRequest = Requests.newModifyRequest( "version: 1", "", "dn: cn=scarter,dc=example,dc=com", "changetype: modify"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); // No changes, nothing to do, the record is not written. assertThat(actual.size()).isEqualTo(0); }
/** * Test SetExcludeBranch method of LDIFChangeRecordWriter. * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetExcludeBranchWrongDN() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final DN dn = DN.valueOf("dc=example.com"); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setExcludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); // Even if DN is wrong then record must be write. assertThat(actual.size()).isEqualTo(14); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); }
/** * Write an DeleteRequest LDIF. Branch is excluded. The record musn't be written. * * @throws Exception */ @Test public void testWriteDeleteRequestBranchExcluded() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final DeleteRequest changeRequest = (DeleteRequest) Requests.newChangeRecord( "version: 1", "dn: uid=scarter,ou=People,dc=example,dc=com", "changetype: delete"); // @formatter:on final DN dn = DN.valueOf("dc=example,dc=com"); writer.setExcludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(0); }
/** * Test to write a record excluding all operational attributes setExcludeAllOperationalAttributes * is forced to true. * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetExcludeAllOperationalAttributesTrue() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setExcludeAllOperationalAttributes(true); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); for (String line : actual) { assertThat(line).doesNotContain("entryUUID"); assertThat(line).doesNotContain("entryDN"); } assertThat(actual.size()).isEqualTo(10); }
/** * Write an ChangeRecord LDIF. * * @throws Exception */ @Test public void testWriteAddRequestBranchExcluded() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final DN dnAdd = DN.valueOf("uid=scarter,ou=People,dc=example,dc=com"); // @formatter:off final ChangeRecord changeRequest = Requests.newAddRequest(dnAdd).addAttribute("sn", "Carter"); // @formatter:on final DN dn = DN.valueOf("dc=example,dc=com"); writer.setExcludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(0); }
/** * Retrieves the backends from the current configuration file. The backends must be enabled to be * listed. No operations should be done within a disabled backend. * * @return A backend list. */ static List<String> getIndexedBackendsFromConfig() { final SearchRequest sr = Requests.newSearchRequest( "", SearchScope.WHOLE_SUBTREE, "(&(objectclass=ds-cfg-pluggable-backend)(ds-cfg-enabled=true))", "ds-cfg-base-dn"); final List<String> listBackends = new LinkedList<>(); try (final EntryReader entryReader = searchConfigFile(sr)) { while (entryReader.hasNext()) { final Entry entry = entryReader.readEntry(); listBackends.addAll(entry.parseAttribute("ds-cfg-base-dn").asSetOfString()); } } catch (Exception ex) { logger.error(LocalizableMessage.raw(ex.getMessage())); } return listBackends; }
/** * Test to write an LDIFChangeRecordWriter with attribute exclusions. In this case, vip attribute * is not present in the example. All lines must be written. * * @throws Exception If the test failed unexpectedly. */ @Test public void testSetExcludeAttributeWithNoMatch() throws Exception { final AttributeDescription attribute = AttributeDescription.valueOf("vip"); final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final AddRequest changeRequest = Requests.newAddRequest(getAddLDIFChangeRecord()); writer.setExcludeAttribute(attribute); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.size()).isEqualTo(14); for (String line : actual) { // we have excluded this attribute especially. assertThat(line).doesNotContain("vip"); } }
/** * Write an ChangeRecord Moddn LDIF. * * @throws Exception */ @Test public void testWriteModdnRequest() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyDNRequest changeRequest = Requests.newModifyDNRequest("uid=scarter,ou=People,dc=example,dc=com", "cn=carter"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(5); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: modrdn"); assertThat(actual.get(2)).isEqualTo("newrdn: cn=carter"); assertThat(actual.get(3)).isEqualTo("deleteoldrdn: 0"); }
/** * Write an AddRequestChange LDIF. The dn/sn is base64 encoded, and contain ascii chars. If they * aren't containing ascii, they will not be translated. * * @throws Exception */ @Test public void testWriteAddBinaryRequest() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final AddRequest changeRequest = Requests.newAddRequest( "dn:: dWlkPXJvZ2FzYXdhcmE=", "changetype: add", "sn::cm9nYXNhd2FyYQ=="); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=rogasawara"); assertThat(actual.get(2)).isEqualTo("sn: rogasawara"); assertThat(actual.get(3)).isEqualTo(""); assertThat(actual.size()).isEqualTo(4); }
/** * Write a delete request. * * @throws Exception */ @Test public void testWriteDeleteRequest() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final DeleteRequest changeRequest = (DeleteRequest) Requests.newChangeRecord( "# Delete an existing entry", "dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com", "changetype: delete"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: cn=Robert Jensen, ou=Marketing, dc=airius, dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: delete"); }
/** * Write an ChangeRecord add type LDIF. * * @throws Exception */ @Test public void testWriteChangeRecord() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ChangeRecord changeRequest = Requests.newChangeRecord( "version: 1", "dn: uid=scarter,ou=People,dc=example,dc=com", "changetype: add", "sn: Carter"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: add"); assertThat(actual.get(2)).isEqualTo("sn: Carter"); assertThat(actual.get(3)).isEqualTo(""); }
/** * Write a modify request containing a control. * * @throws Exception */ @Test public void testWriteModifyRequestUsingControl() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyRequest changeRequest = Requests.newModifyRequest("cn=scarter,dc=example,dc=com") .addControl(PreReadRequestControl.newControl(true, "mail")) .addModification(ModificationType.REPLACE, "mail", "*****@*****.**"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(7); assertThat(actual.get(0)).isEqualTo("dn: cn=scarter,dc=example,dc=com"); assertThat(actual.get(1)).contains("control: 1.3.6.1.1.13.1 true:"); assertThat(actual.get(2)).isEqualTo("changetype: modify"); assertThat(actual.get(3)).isEqualTo("replace: mail"); assertThat(actual.get(4)).isEqualTo("mail: [email protected]"); assertThat(actual.get(5)).isEqualTo("-"); }
/** * Write a modifyDN request with a branch exclusion. * * @throws Exception */ @Test public void testWriteModifyDNRequestBranchExcludedMatch() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyDNRequest changeRequest = (ModifyDNRequest) Requests.newChangeRecord( "version: 1", "dn: cn=scarter,dc=example,dc=com", "changetype: modrdn", "newrdn: cn=Susan Jacobs", "deleteoldrdn: no"); // @formatter:on final DN dn = DN.valueOf("dc=example,dc=com"); writer.setExcludeBranch(dn); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.size()).isEqualTo(0); }
/** * Write an ChangeRecord Moddn LDIF. * * @throws Exception */ @Test public void testWriteModdnRequestNewSuperior() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); // @formatter:off final ModifyDNRequest changeRequest = (ModifyDNRequest) Requests.newChangeRecord( "version: 1", "dn: uid=scarter,ou=People,dc=example,dc=com", "changetype: moddn", "newrdn: cn=carter", "deleteoldrdn: true", "newsuperior: ou=People,dc=example,dc=org"); // @formatter:on writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.size()).isEqualTo(6); assertThat(actual.get(3)).isEqualTo("deleteoldrdn: 1"); assertThat(actual.get(4)).isEqualTo("newsuperior: ou=People,dc=example,dc=org"); }
/** * Test the LDIFWriteChangeRecord using an output file verifying write is correctly invoked. * * @throws Exception If the test failed unexpectedly. */ @Test public void testWriteEntryOutputStreamUsingByteArrayOutputStream() throws Exception { final OutputStream out = new ByteArrayOutputStream(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(out); // @formatter:off final String[] lines = {"dn: cn=scarter,dc=example,dc=com", "changetype: add", "sn: Carter"}; // @formatter:on final AddRequest changeRequest = Requests.newAddRequest(lines); writer.writeChangeRecord(changeRequest); writer.close(); BufferedReader reader = new BufferedReader( new InputStreamReader(new ByteArrayInputStream(out.toString().getBytes()))); assertThat(reader.readLine()).isEqualTo(lines[0]); assertThat(reader.readLine()).isEqualTo(lines[1]); assertThat(reader.readLine()).isEqualTo(lines[2]); assertThat(reader.readLine()).isEqualTo(""); assertThat(reader.readLine()).isNull(); }
/** * Write a record containing multiple changes. * * @throws Exception */ @Test public void testWriteMultipleChangesRecord() throws Exception { // @formatter:off final ChangeRecord changeRequest = Requests.newChangeRecord( "dn: uid=scarter,ou=People,dc=example,dc=com", "changetype: modify", "add: work-phone", "work-phone: 650/506-7000", "work-phone: 650/506-7001", "-", "delete: home-fax", "-", "replace: home-phone", "home-phone: 415/697-8899"); // @formatter:on final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); writer.writeChangeRecord(changeRequest); writer.close(); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.size()).isGreaterThan(10); }
/** * Test to write a change record containing an URL. * * @throws Exception */ @Test public void testWriteAddRequestJpegAttributeOk() throws Exception { final List<String> actual = new ArrayList<>(); final LDIFChangeRecordWriter writer = new LDIFChangeRecordWriter(actual); final File file = File.createTempFile("sdk", ".jpeg"); final String url = file.toURI().toURL().toString(); final DN dnAdd = DN.valueOf("uid=scarter,ou=People,dc=example,dc=com"); // @formatter:off final ChangeRecord changeRequest = Requests.newAddRequest(dnAdd).addAttribute("sn", "Carter").addAttribute("jpegphoto", url); // @formatter:on writer.writeChangeRecord(changeRequest); file.delete(); writer.close(); assertThat(actual.size()).isEqualTo(5); assertThat(actual.get(0)).isEqualTo("dn: uid=scarter,ou=People,dc=example,dc=com"); assertThat(actual.get(1)).isEqualTo("changetype: add"); assertThat(actual.get(2)).isEqualTo("sn: Carter"); assertThat(actual.get(3)).contains("jpegphoto: file:/"); }
/** * Updates the config file during the upgrade process. * * @param configPath The original path to the file. * @param filter The filter to select entries. Only useful for modify change type. * @param changeType The change type which must be applied to ldif lines. * @param ldifLines The change record ldif lines. For ADD change type, the first line must be the * dn. For DELETE change type, the first and only line must be the dn. * @throws IOException If an Exception occurs during the input output methods. * @return The changes number that have occurred. */ static int updateConfigFile( final String configPath, final Filter filter, final ChangeOperationType changeType, final String... ldifLines) throws IOException { final File original = new File(configPath); final File copyConfig = File.createTempFile("copyConfig", ".tmp", original.getParentFile()); int changeCount = 0; LDIFEntryReader entryReader = null; LDIFEntryWriter writer = null; try { final Schema schema = getUpgradeSchema(); entryReader = new LDIFEntryReader(new FileInputStream(configPath)).setSchema(schema); writer = new LDIFEntryWriter(new FileOutputStream(copyConfig)); writer.setWrapColumn(80); // Writes the header on the new file. writer.writeComment(INFO_CONFIG_FILE_HEADER.get()); writer.setWrapColumn(0); boolean entryAlreadyExist = false; DN ldifDN = null; if (filter == null && (changeType == ADD || changeType == DELETE)) { // The first line should start with dn: ldifDN = DN.valueOf(ldifLines[0].replaceFirst("dn: ", "")); } final Filter f = filter != null ? filter : Filter.alwaysFalse(); final Matcher matcher = f.matcher(schema); while (entryReader.hasNext()) { Entry entry = entryReader.readEntry(); final DN entryDN = entry.getName(); // Searching for the related entries if (changeType == MODIFY && matcher.matches(entry) == ConditionResult.TRUE) { try { final ModifyRequest mr = Requests.newModifyRequest(readLDIFLines(entryDN, changeType, ldifLines)); entry = Entries.modifyEntryPermissive(entry, mr.getModifications()); changeCount++; logger.debug( LocalizableMessage.raw("The following entry has been modified : %s", entryDN)); } catch (Exception ex) { logger.error(LocalizableMessage.raw(ex.getMessage())); } } if (entryDN.equals(ldifDN)) { logger.debug(LocalizableMessage.raw("Entry %s found", entryDN)); entryAlreadyExist = true; if (changeType == DELETE) { entry = null; changeCount++; logger.debug( LocalizableMessage.raw("The following entry has been deleted : %s", entryDN)); } } if (entry != null) { writer.writeEntry(entry); } } if (changeType == ADD && !entryAlreadyExist) { final AddRequest ar = Requests.newAddRequest(ldifLines); writer.writeEntry(ar); logger.debug( LocalizableMessage.raw( "Entry successfully added %s in %s", ldifDN, original.getAbsolutePath())); changeCount++; } } catch (Exception ex) { throw new IOException(ex.getMessage()); } finally { // The reader and writer must be close before renaming files. // Otherwise it causes exceptions under windows OS. StaticUtils.close(entryReader, writer); } try { // Renaming the file, overwriting previous one. rename(copyConfig, new File(configPath)); } catch (IOException e) { logger.error(LocalizableMessage.raw(e.getMessage())); deleteRecursively(original); throw e; } return changeCount; }
/** * The root DSE is a DSA-specific Entry (DSE) and not part of any naming context (or any subtree), * and which is uniquely identified by the empty DN. * * <p>A Directory Server uses the root DSE to provide information about itself using the following * set of attributes: * * <ul> * <li>{@code altServer}: alternative Directory Servers * <li>{@code namingContexts}: naming contexts * <li>{@code supportedControl}: recognized LDAP controls * <li>{@code supportedExtension}: recognized LDAP extended operations * <li>{@code supportedFeatures}: recognized LDAP features * <li>{@code supportedLDAPVersion}: LDAP versions supported * <li>{@code supportedSASLMechanisms}: recognized SASL authentication mechanisms * <li>{@code supportedAuthPasswordSchemes}: recognized authentication password schemes * <li>{@code subschemaSubentry}: the name of the subschema subentry holding the schema * controlling the Root DSE * <li>{@code vendorName}: the name of the Directory Server implementer * <li>{@code vendorVersion}: the version of the Directory Server implementation. * </ul> * * The values provided for these attributes may depend on session- specific and other factors. For * example, a server supporting the SASL EXTERNAL mechanism might only list "EXTERNAL" when the * client's identity has been established by a lower level. * * <p>The root DSE may also include a {@code subschemaSubentry} attribute. If it does, the attribute * refers to the subschema (sub)entry holding the schema controlling the root DSE. Clients SHOULD * NOT assume that this subschema (sub)entry controls other entries held by the server. * * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight Directory Access * Protocol (LDAP): Directory Information Models </a> * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing Vendor Information in the * LDAP Root DSE </a> * @see <a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP Authentication Password Schema * </a> */ public final class RootDSE { private static final AttributeDescription ATTR_ALT_SERVER = AttributeDescription.create(CoreSchema.getAltServerAttributeType()); private static final AttributeDescription ATTR_NAMING_CONTEXTS = AttributeDescription.create(CoreSchema.getNamingContextsAttributeType()); private static final AttributeDescription ATTR_SUBSCHEMA_SUBENTRY = AttributeDescription.create(CoreSchema.getSubschemaSubentryAttributeType()); private static final AttributeDescription ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES = AttributeDescription.create(CoreSchema.getSupportedAuthPasswordSchemesAttributeType()); private static final AttributeDescription ATTR_SUPPORTED_CONTROL = AttributeDescription.create(CoreSchema.getSupportedControlAttributeType()); private static final AttributeDescription ATTR_SUPPORTED_EXTENSION = AttributeDescription.create(CoreSchema.getSupportedExtensionAttributeType()); private static final AttributeDescription ATTR_SUPPORTED_FEATURE = AttributeDescription.create(CoreSchema.getSupportedFeaturesAttributeType()); private static final AttributeDescription ATTR_SUPPORTED_LDAP_VERSION = AttributeDescription.create(CoreSchema.getSupportedLDAPVersionAttributeType()); private static final AttributeDescription ATTR_SUPPORTED_SASL_MECHANISMS = AttributeDescription.create(CoreSchema.getSupportedSASLMechanismsAttributeType()); private static final AttributeDescription ATTR_VENDOR_NAME = AttributeDescription.create(CoreSchema.getVendorNameAttributeType()); private static final AttributeDescription ATTR_VENDOR_VERSION = AttributeDescription.create(CoreSchema.getVendorNameAttributeType()); private static final AttributeDescription ATTR_FULL_VENDOR_VERSION = AttributeDescription.create(CoreSchema.getFullVendorVersionAttributeType()); private static final SearchRequest SEARCH_REQUEST = Requests.newSearchRequest( DN.rootDN(), SearchScope.BASE_OBJECT, Filter.objectClassPresent(), ATTR_ALT_SERVER.toString(), ATTR_NAMING_CONTEXTS.toString(), ATTR_SUPPORTED_CONTROL.toString(), ATTR_SUPPORTED_EXTENSION.toString(), ATTR_SUPPORTED_FEATURE.toString(), ATTR_SUPPORTED_LDAP_VERSION.toString(), ATTR_SUPPORTED_SASL_MECHANISMS.toString(), ATTR_FULL_VENDOR_VERSION.toString(), ATTR_VENDOR_NAME.toString(), ATTR_VENDOR_VERSION.toString(), ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES.toString(), ATTR_SUBSCHEMA_SUBENTRY.toString(), "*"); /** * Asynchronously reads the Root DSE from the Directory Server using the provided connection. * * <p>If the Root DSE is not returned by the Directory Server then the request will fail with an * {@link EntryNotFoundException}. More specifically, the returned promise will never return * {@code null}. * * @param connection A connection to the Directory Server whose Root DSE is to be read. * @param handler A result handler which can be used to asynchronously process the operation * result when it is received, may be {@code null}. * @return A promise representing the result of the operation. * @throws UnsupportedOperationException If the connection does not support search operations. * @throws IllegalStateException If the connection has already been closed, i.e. if {@code * isClosed() == true}. * @throws NullPointerException If the {@code connection} was {@code null}. */ public static LdapPromise<RootDSE> readRootDSEAsync( final Connection connection, final ResultHandler<? super RootDSE> handler) { return connection .searchSingleEntryAsync(SEARCH_REQUEST) .then( new Function<SearchResultEntry, RootDSE, LdapException>() { @Override public RootDSE apply(SearchResultEntry result) { return valueOf(result); } }); } /** * Reads the Root DSE from the Directory Server using the provided connection. * * <p>If the Root DSE is not returned by the Directory Server then the request will fail with an * {@link EntryNotFoundException}. More specifically, this method will never return {@code null}. * * @param connection A connection to the Directory Server whose Root DSE is to be read. * @return The Directory Server's Root DSE. * @throws LdapException If the result code indicates that the request failed for some reason. * @throws UnsupportedOperationException If the connection does not support search operations. * @throws IllegalStateException If the connection has already been closed, i.e. if {@code * isClosed() == true}. * @throws NullPointerException If the {@code connection} was {@code null}. */ public static RootDSE readRootDSE(final Connection connection) throws LdapException { final Entry entry = connection.searchSingleEntry(SEARCH_REQUEST); return valueOf(entry); } /** * Creates a new Root DSE instance backed by the provided entry. Modifications made to {@code * entry} will be reflected in the returned Root DSE. The returned Root DSE instance is * unmodifiable and attempts to use modify any of the returned collections will result in a {@code * UnsupportedOperationException}. * * @param entry The Root DSE entry. * @return A Root DSE instance backed by the provided entry. * @throws NullPointerException If {@code entry} was {@code null} . */ public static RootDSE valueOf(Entry entry) { Reject.ifNull(entry); return new RootDSE(entry); } private final Entry entry; /** Prevent direct instantiation. */ private RootDSE(final Entry entry) { this.entry = entry; } /** * Returns an unmodifiable list of URIs referring to alternative Directory Servers that may be * contacted when the Directory Server becomes unavailable. * * <p>URIs for Directory Servers implementing the LDAP protocol are written according to RFC 4516. * Other kinds of URIs may be provided. * * <p>If the Directory Server does not know of any other Directory Servers that could be used, the * returned list will be empty. * * @return An unmodifiable list of URIs referring to alternative Directory Servers, which may be * empty. * @see <a href="http://tools.ietf.org/html/rfc4516">RFC 4516 - Lightweight Directory Access * Protocol (LDAP): Uniform Resource Locator </a> */ public Collection<String> getAlternativeServers() { return getMultiValuedAttribute(ATTR_ALT_SERVER, Functions.byteStringToString()); } /** * Returns the entry which backs this Root DSE instance. Modifications made to the returned entry * will be reflected in this Root DSE. * * @return The underlying Root DSE entry. */ public Entry getEntry() { return entry; } /** * Returns an unmodifiable list of DNs identifying the context prefixes of the naming contexts * that the Directory Server masters or shadows (in part or in whole). * * <p>If the Directory Server does not master or shadow any naming contexts, the returned list * will be empty. * * @return An unmodifiable list of DNs identifying the context prefixes of the naming contexts, * which may be empty. */ public Collection<DN> getNamingContexts() { return getMultiValuedAttribute(ATTR_NAMING_CONTEXTS, Functions.byteStringToDN()); } /** * Returns a string which represents the DN of the subschema subentry holding the schema * controlling the Root DSE. * * <p>Clients SHOULD NOT assume that this subschema (sub)entry controls other entries held by the * Directory Server. * * @return The DN of the subschema subentry holding the schema controlling the Root DSE, or {@code * null} if the DN is not provided. */ public DN getSubschemaSubentry() { return getSingleValuedAttribute(ATTR_SUBSCHEMA_SUBENTRY, Functions.byteStringToDN()); } /** * Returns an unmodifiable list of supported authentication password schemes which the Directory * Server supports. * * <p>If the Directory Server does not support any authentication password schemes, the returned * list will be empty. * * @return An unmodifiable list of supported authentication password schemes, which may be empty. * @see <a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP Authentication Password * Schema </a> */ public Collection<String> getSupportedAuthenticationPasswordSchemes() { return getMultiValuedAttribute( ATTR_SUPPORTED_AUTH_PASSWORD_SCHEMES, Functions.byteStringToString()); } /** * Returns an unmodifiable list of object identifiers identifying the request controls that the * Directory Server supports. * * <p>If the Directory Server does not support any request controls, the returned list will be * empty. Object identifiers identifying response controls may not be listed. * * @return An unmodifiable list of object identifiers identifying the request controls, which may * be empty. */ public Collection<String> getSupportedControls() { return getMultiValuedAttribute(ATTR_SUPPORTED_CONTROL, Functions.byteStringToString()); } /** * Returns an unmodifiable list of object identifiers identifying the extended operations that the * Directory Server supports. * * <p>If the Directory Server does not support any extended operations, the returned list will be * empty. * * <p>An extended operation generally consists of an extended request and an extended response but * may also include other protocol data units (such as intermediate responses). The object * identifier assigned to the extended request is used to identify the extended operation. Other * object identifiers used in the extended operation may not be listed as values of this * attribute. * * @return An unmodifiable list of object identifiers identifying the extended operations, which * may be empty. */ public Collection<String> getSupportedExtendedOperations() { return getMultiValuedAttribute(ATTR_SUPPORTED_EXTENSION, Functions.byteStringToString()); } /** * Returns an unmodifiable list of object identifiers identifying elective features that the * Directory Server supports. * * <p>If the server does not support any discoverable elective features, the returned list will be * empty. * * @return An unmodifiable list of object identifiers identifying the elective features, which may * be empty. */ public Collection<String> getSupportedFeatures() { return getMultiValuedAttribute(ATTR_SUPPORTED_FEATURE, Functions.byteStringToString()); } /** * Returns an unmodifiable list of the versions of LDAP that the Directory Server supports. * * @return An unmodifiable list of the versions. */ public Collection<Integer> getSupportedLDAPVersions() { return getMultiValuedAttribute(ATTR_SUPPORTED_LDAP_VERSION, Functions.byteStringToInteger()); } /** * Returns an unmodifiable list of the SASL mechanisms that the Directory Server recognizes and/or * supports. * * <p>The contents of the returned list may depend on the current session state and may be empty * if the Directory Server does not support any SASL mechanisms. * * @return An unmodifiable list of the SASL mechanisms, which may be empty. * @see <a href="http://tools.ietf.org/html/rfc4513">RFC 4513 - Lightweight Directory Access * Protocol (LDAP): Authentication Methods and Security Mechanisms </a> * @see <a href="http://tools.ietf.org/html/rfc4422">RFC 4422 - Simple Authentication and Security * Layer (SASL) </a> */ public Collection<String> getSupportedSASLMechanisms() { return getMultiValuedAttribute(ATTR_SUPPORTED_SASL_MECHANISMS, Functions.byteStringToString()); } /** * Returns a string which represents the name of the Directory Server implementer. * * @return The name of the Directory Server implementer, or {@code null} if the vendor name is not * provided. * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing Vendor Information in the * LDAP Root DSE </a> */ public String getVendorName() { return getSingleValuedAttribute(ATTR_VENDOR_NAME, Functions.byteStringToString()); } /** * Returns a string which represents the version of the Directory Server implementation. * * <p>Note that this value is typically a release value comprised of a string and/or a string of * numbers used by the developer of the LDAP server product. The returned string will be unique * between two versions of the Directory Server, but there are no other syntactic restrictions on * the value or the way it is formatted. * * @return The version of the Directory Server implementation, or {@code null} if the vendor * version is not provided. * @see <a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing Vendor Information in the * LDAP Root DSE </a> */ public String getVendorVersion() { return getSingleValuedAttribute(ATTR_VENDOR_VERSION, Functions.byteStringToString()); } /** * Returns a string which represents the full version of the Directory Server implementation. * * @return The full version of the Directory Server implementation, or {@code null} if the vendor * version is not provided. */ public String getFullVendorVersion() { return getSingleValuedAttribute(ATTR_FULL_VENDOR_VERSION, Functions.byteStringToString()); } private <N> Collection<N> getMultiValuedAttribute( final AttributeDescription attributeDescription, final Function<ByteString, N, NeverThrowsException> function) { // The returned collection is unmodifiable because we may need to // return an empty collection if the attribute does not exist in the // underlying entry. If a value is then added to the returned empty // collection it would require that an attribute is created in the // underlying entry in order to maintain consistency. final Attribute attr = entry.getAttribute(attributeDescription); if (attr != null) { return Collections.unmodifiableCollection( Collections2.transformedCollection(attr, function, Functions.objectToByteString())); } else { return Collections.emptySet(); } } private <N> N getSingleValuedAttribute( final AttributeDescription attributeDescription, final Function<ByteString, N, NeverThrowsException> function) { final Attribute attr = entry.getAttribute(attributeDescription); if (attr == null || attr.isEmpty()) { return null; } else { return function.apply(attr.firstValue()); } } }