/**
   * 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("");
  }
예제 #5
0
  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);
  }
예제 #17
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:/");
  }
예제 #29
0
  /**
   * 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;
  }
예제 #30
0
/**
 * 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());
    }
  }
}