/**
   * Tests the use of the StartTLS extended operation to communicate with the server in conjunction
   * with no authentication and using blind trust.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test
  public void testStartTLSNoAuthTrustAll() throws Exception {
    try {
      DirectoryServer.setRejectUnauthenticatedRequests(true);

      String[] argSearch = {
        "--noPropertiesFile",
        "-h",
        "127.0.0.1",
        "-p",
        String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-D",
        "cn=directory manager",
        "-w",
        "password",
        "-q",
        "-X",
        "-b",
        "",
        "-s",
        "base",
        "(objectClass=*)"
      };
      assertEquals(LDAPSearch.mainSearch(argSearch, false, null, System.err), 0);
    } finally {
      DirectoryServer.setRejectUnauthenticatedRequests(false);
    }
  }
  /**
   * Tests whether the who am I? extended operation with an unauthenticated connection fails with
   * new setting of "ds-cfg-reject-unauthenticated-requests".
   *
   * @throws UnsupportedEncodingException If an unexpected problem occurs.
   * @throws IOException If an unexpected problem occurs.
   * @throws ClientException If an unexpected problem occurs.
   */
  @Test
  public void testUnauthWAINewCfg()
      throws UnsupportedEncodingException, IOException, ClientException {
    try {
      DirectoryServer.setRejectUnauthenticatedRequests(true);

      Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
      LDAPReader reader = new LDAPReader(s);
      LDAPWriter writer = new LDAPWriter(s);
      AtomicInteger nextMessageID = new AtomicInteger(1);
      LDAPAuthenticationHandler authHandler =
          new LDAPAuthenticationHandler(reader, writer, "localhost", nextMessageID);
      ByteString authzID = null;
      try {
        authzID = authHandler.requestAuthorizationIdentity();
      } catch (LDAPException e) {
        assertNull(authzID);
      } finally {
        LDAPMessage unbindMessage =
            new LDAPMessage(nextMessageID.getAndIncrement(), new UnbindRequestProtocolOp());
        writer.writeMessage(unbindMessage);
        s.close();
      }
    } finally {
      DirectoryServer.setRejectUnauthenticatedRequests(false);
    }
  }
  @BeforeClass()
  public void setUp() throws Exception {
    TestCaseUtils.startServer();
    TestCaseUtils.clearJEBackend(false, "userRoot", SUFFIX);

    InternalClientConnection connection = InternalClientConnection.getRootConnection();

    // Add suffix entry.
    DN suffixDN = DN.decode(SUFFIX);
    if (DirectoryServer.getEntry(suffixDN) == null) {
      Entry suffixEntry = StaticUtils.createEntry(suffixDN);
      AddOperation addOperation =
          connection.processAdd(
              suffixEntry.getDN(),
              suffixEntry.getObjectClasses(),
              suffixEntry.getUserAttributes(),
              suffixEntry.getOperationalAttributes());
      assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
      assertNotNull(DirectoryServer.getEntry(suffixEntry.getDN()));
    }

    // Add base entry.
    DN baseDN = DN.decode(BASE);
    if (DirectoryServer.getEntry(baseDN) == null) {
      Entry baseEntry = StaticUtils.createEntry(baseDN);
      AddOperation addOperation =
          connection.processAdd(
              baseEntry.getDN(),
              baseEntry.getObjectClasses(),
              baseEntry.getUserAttributes(),
              baseEntry.getOperationalAttributes());
      assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
      assertNotNull(DirectoryServer.getEntry(baseEntry.getDN()));
    }

    // Add test entry.
    Entry testEntry =
        TestCaseUtils.makeEntry(
            "dn: uid=rogasawara," + BASE,
            "objectclass: top",
            "objectclass: person",
            "objectclass: organizationalPerson",
            "objectclass: inetOrgPerson",
            "uid: rogasawara",
            "userpassword: password",
            "mail: [email protected]",
            "givenname: Rodney",
            "sn: Ogasawara",
            "cn: Rodney Ogasawara",
            "title: Sales, Director");
    AddOperation addOperation =
        connection.processAdd(
            testEntry.getDN(),
            testEntry.getObjectClasses(),
            testEntry.getUserAttributes(),
            testEntry.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(DirectoryServer.getEntry(testEntry.getDN()));
  }
  /**
   * Tests whether the Who Am I? extended operation with an internal authenticated connection
   * succeeds with new setting of "ds-cfg-reject-unauthenticated-requests".
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test
  public void testAuthWAINewCfg() throws Exception {
    try {
      DirectoryServer.setRejectUnauthenticatedRequests(true);

      InternalClientConnection conn = InternalClientConnection.getRootConnection();
      ExtendedOperation extOp = conn.processExtendedOperation(OID_WHO_AM_I_REQUEST, null);
      assertEquals(extOp.getResultCode(), ResultCode.SUCCESS);
      assertNotNull(extOp.getResponseValue());
    } finally {
      DirectoryServer.setRejectUnauthenticatedRequests(false);
    }
  }
  /**
   * Tests whether the Who Am I? extended operation with an internal authenticated connection
   * succeeds with default setting of "ds-cfg-reject-unauthenticated-requests".
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test()
  public void testAuthWAIDefCfg() throws Exception {
    DirectoryServer.setRejectUnauthenticatedRequests(false);

    Socket s = new Socket("127.0.0.1", TestCaseUtils.getServerLdapPort());
    LDAPReader reader = new LDAPReader(s);
    LDAPWriter writer = new LDAPWriter(s);

    AtomicInteger nextMessageID = new AtomicInteger(1);
    LDAPAuthenticationHandler authHandler =
        new LDAPAuthenticationHandler(reader, writer, "localhost", nextMessageID);
    authHandler.doSimpleBind(
        3,
        ByteString.valueOf("cn=Directory Manager"),
        ByteString.valueOf("password"),
        new ArrayList<Control>(),
        new ArrayList<Control>());
    ByteString authzID = authHandler.requestAuthorizationIdentity();
    assertNotNull(authzID);

    LDAPMessage unbindMessage =
        new LDAPMessage(nextMessageID.getAndIncrement(), new UnbindRequestProtocolOp());
    writer.writeMessage(unbindMessage);
    s.close();
  }
  /**
   * Tests whether an Unauthenticated BIND request will be allowed with the default configuration
   * settings for "ds-cfg-reject-unauthenticated-requests".
   */
  @Test()
  public void testUnauthBindDefCfg() {
    DirectoryServer.setRejectUnauthenticatedRequests(false);

    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation = conn.processSimpleBind(DN.nullDN(), null);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the whether the unauthenticated ADD,MODIFY,COMPARE,MODRDN and DELETE requests fail with
   * the new configuration settings.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test
  public void testOtherOpsUnauthNewCfg() throws Exception {
    try {
      DirectoryServer.setRejectUnauthenticatedRequests(true);

      assertFalse(performAddOperation(false) == 0);

      assertFalse(performModifyOperation(false) == 0);

      assertFalse(performCompareOperation(false) == 0);

      assertFalse(performModRdnOperation(false) == 0);

      assertFalse(performDeleteOperation(false) == 0);
    } finally {
      DirectoryServer.setRejectUnauthenticatedRequests(false);
    }
  }
  /**
   * Tests whether authenticated and unauthenticated BIND requests will be allowed with the new
   * configuration settings for "ds-cfg-reject-unauthenticated-requests" .
   */
  @Test
  public void testBindNewCfg() {
    try {
      DirectoryServer.setRejectUnauthenticatedRequests(true);

      InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
      ByteString user = ByteString.valueOf("cn=Directory Manager");
      ByteString password = ByteString.valueOf("password");
      // Unauthenticated BIND request.
      BindOperation bindOperation = conn.processSimpleBind(DN.nullDN(), null);
      assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
      // Authenticated BIND request.
      bindOperation = conn.processSimpleBind(user, password);
      assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    } finally {
      DirectoryServer.setRejectUnauthenticatedRequests(false);
    }
  }
  /**
   * Tests whether an authenticated BIND request will be allowed with the default configuration
   * settings for "ds-cfg-reject-unauthenticated-requests" .
   */
  @Test()
  public void testAuthBindDefCfg() {
    DirectoryServer.setRejectUnauthenticatedRequests(false);

    InternalClientConnection conn = new InternalClientConnection(new AuthenticationInfo());
    ByteString user = ByteString.valueOf("cn=Directory Manager");
    ByteString password = ByteString.valueOf("password");
    BindOperation bindOperation = conn.processSimpleBind(user, password);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Ensures that password policy creation will fail when given an invalid configuration.
   *
   * @param e The entry containing an invalid password policy configuration.
   * @throws Exception If an unexpected problem occurs.
   */
  @Test(dataProvider = "invalidConfigs")
  public void testInvalidConfigurations(Entry e) throws Exception {
    InternalClientConnection connection = InternalClientConnection.getRootConnection();

    AddOperation addOperation =
        connection.processAdd(
            e.getDN(), e.getObjectClasses(), e.getUserAttributes(), e.getOperationalAttributes());
    assertTrue(addOperation.getResultCode() != ResultCode.SUCCESS);
    assertNull(DirectoryServer.getEntry(e.getDN()));
  }
  /**
   * Tests whether both authenticated and unauthenticated SEARCH requests will be allowed with the
   * new configuration settings for "ds-cfg-reject-unauthenticated-requests" .
   */
  @Test
  public void testSearchNewCfg() {
    try {
      DirectoryServer.setRejectUnauthenticatedRequests(true);

      String[] args = {
        "--noPropertiesFile",
        "-h",
        "127.0.0.1",
        "-p",
        String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-b",
        "",
        "-s",
        "base",
        "(objectClass=*)"
      };

      assertFalse(LDAPSearch.mainSearch(args, false, null, null) == 0);

      String[] authArgs = {
        "--noPropertiesFile",
        "-h",
        "127.0.0.1",
        "-p",
        String.valueOf(TestCaseUtils.getServerLdapPort()),
        "-D",
        "cn=Directory Manager",
        "-w",
        "password",
        "-b",
        "",
        "-s",
        "base",
        "(objectClass=*)"
      };
      assertEquals(LDAPSearch.mainSearch(authArgs, false, null, System.err), 0);
    } finally {
      DirectoryServer.setRejectUnauthenticatedRequests(false);
    }
  }
  /**
   * Tests the whether the authenticated ADD,MODIFY,COMPARE,MODRDN and DELETE requests succeed with
   * the default configuration settings.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test()
  public void testOtherOpsAuthDefCfg() throws Exception {
    DirectoryServer.setRejectUnauthenticatedRequests(false);

    assertEquals(performAddOperation(true), 0);

    assertEquals(performModifyOperation(true), 0);

    assertEquals(performCompareOperation(true), 0);

    assertEquals(performModRdnOperation(true), 0);

    assertEquals(performDeleteOperation(true), 0);
  }
  /**
   * Tests whether an unauthenticated SEARCH request will be allowed with the default configuration
   * settings for "ds-cfg-reject-unauthenticated-requests".
   */
  @Test()
  public void testUnauthSearchDefCfg() {
    DirectoryServer.setRejectUnauthenticatedRequests(false);

    String[] args = {
      "--noPropertiesFile",
      "-h",
      "127.0.0.1",
      "-p",
      String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-b",
      "",
      "-s",
      "base",
      "(objectClass=*)"
    };

    assertEquals(LDAPSearch.mainSearch(args, false, null, System.err), 0);
  }
  /**
   * Ensures that password policy pwdPolicySubentry operational attribute reflects active password
   * policy for a given user entry.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test()
  public void testPasswordPolicySubentryAttribute() throws Exception {
    PasswordPolicy defaultPolicy = DirectoryServer.getDefaultPasswordPolicy();
    assertNotNull(defaultPolicy);

    Entry testEntry = DirectoryServer.getEntry(DN.decode("uid=rogasawara," + BASE));
    assertNotNull(testEntry);

    AttributeType attrType = DirectoryServer.getAttributeType("pwdpolicysubentry");

    // Make sure that default policy is in effect
    // for the user entry.
    assertTrue(testEntry.hasAttribute(attrType));
    assertTrue(
        testEntry.hasValue(
            attrType, null, AttributeValues.create(attrType, defaultPolicy.getDN().toString())));

    // Add new subentry policy with the
    // scope to apply to the user entry.
    Entry policyEntry =
        TestCaseUtils.makeEntry(
            "dn: cn=Temp Policy," + SUFFIX,
            "objectClass: top",
            "objectClass: pwdPolicy",
            "objectClass: subentry",
            "cn: Temp Policy",
            "subtreeSpecification: { base \"ou=people\" }",
            "pwdLockoutDuration: 300",
            "pwdMaxFailure: 3",
            "pwdMustChange: true",
            "pwdAttribute: userPassword");

    InternalClientConnection connection = InternalClientConnection.getRootConnection();

    AddOperation addOperation =
        connection.processAdd(
            policyEntry.getDN(),
            policyEntry.getObjectClasses(),
            policyEntry.getUserAttributes(),
            policyEntry.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(DirectoryServer.getEntry(policyEntry.getDN()));

    // Make sure just added policy is in effect.
    testEntry = DirectoryServer.getEntry(DN.decode("uid=rogasawara," + BASE));
    assertNotNull(testEntry);

    assertTrue(testEntry.hasAttribute(attrType));
    assertTrue(
        testEntry.hasValue(
            attrType, null, AttributeValues.create(attrType, "cn=Temp Policy," + SUFFIX)));

    // Remove subentry policy and make sure
    // default policy is in effect again.
    TestCaseUtils.deleteEntry(policyEntry.getDN());

    testEntry = DirectoryServer.getEntry(DN.decode("uid=rogasawara," + BASE));
    assertNotNull(testEntry);

    assertTrue(testEntry.hasAttribute(attrType));
    assertTrue(
        testEntry.hasValue(
            attrType, null, AttributeValues.create(attrType, defaultPolicy.getDN().toString())));
  }
  /**
   * Ensures that password policy constructed from subentry, containing a password validator
   * reference, is active and has a valid configuration.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test()
  public void testValidConfigurationWithValidator() throws Exception {
    PasswordPolicy defaultPolicy = DirectoryServer.getDefaultPasswordPolicy();
    assertNotNull(defaultPolicy);

    // The values are selected on a basis that they
    // should differ from default password policy.
    Entry policyEntry =
        TestCaseUtils.makeEntry(
            "dn: cn=Temp Validator Policy," + SUFFIX,
            "objectClass: top",
            "objectClass: pwdPolicy",
            "objectClass: pwdValidatorPolicy",
            "objectClass: subentry",
            "cn: Temp Policy",
            "subtreeSpecification: { base \"ou=people\" }",
            "pwdLockoutDuration: 300",
            "pwdMaxFailure: 3",
            "pwdMustChange: TRUE",
            "pwdAttribute: authPassword",
            "pwdMinAge: 600",
            "pwdMaxAge: 2147483647",
            "pwdInHistory: 5",
            "pwdExpireWarning: 864000",
            "pwdGraceAuthNLimit: 3",
            "pwdFailureCountInterval: 3600",
            "pwdAllowUserChange: FALSE",
            "pwdSafeModify: TRUE",
            "ds-cfg-password-validator: cn=Unique Characters,cn=Password Validators,cn=config",
            "ds-cfg-password-validator: cn=Length-Based Password Validator,cn=Password Validators,cn=config");

    InternalClientConnection connection = InternalClientConnection.getRootConnection();

    AddOperation addOperation =
        connection.processAdd(
            policyEntry.getDN(),
            policyEntry.getObjectClasses(),
            policyEntry.getUserAttributes(),
            policyEntry.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(DirectoryServer.getEntry(policyEntry.getDN()));

    PasswordPolicy policy =
        (PasswordPolicy)
            DirectoryServer.getAuthenticationPolicy(
                DN.decode("cn=Temp Validator Policy," + SUFFIX));
    assertNotNull(policy);

    // Check the password validator attributes for correct values.
    Collection<PasswordValidator<?>> validators = policy.getPasswordValidators();
    assertFalse(validators.isEmpty());
    assertEquals(validators.size(), 2);

    // Make sure this policy applies to the test entry
    // its supposed to target and that its the same
    // policy object as previously tested.
    Entry testEntry = DirectoryServer.getEntry(DN.decode("uid=rogasawara," + BASE));
    assertNotNull(testEntry);

    AuthenticationPolicy statePolicy = AuthenticationPolicy.forUser(testEntry, false);
    assertNotNull(statePolicy);
    assertEquals(policy, statePolicy);

    // Make sure this policy is gone and default
    // policy is in effect instead.
    TestCaseUtils.deleteEntry(policyEntry.getDN());
    statePolicy = AuthenticationPolicy.forUser(testEntry, false);
    assertNotNull(statePolicy);
    assertEquals(defaultPolicy, statePolicy);
  }
  /**
   * Ensures that password policy constructed from subentry is active and has a valid configuration.
   *
   * @throws Exception If an unexpected problem occurs.
   */
  @Test()
  public void testValidConfiguration() throws Exception {
    PasswordPolicy defaultPolicy = DirectoryServer.getDefaultPasswordPolicy();
    assertNotNull(defaultPolicy);

    // The values are selected on a basis that they
    // should differ from default password policy.
    Entry policyEntry =
        TestCaseUtils.makeEntry(
            "dn: cn=Temp Policy," + SUFFIX,
            "objectClass: top",
            "objectClass: pwdPolicy",
            "objectClass: subentry",
            "cn: Temp Policy",
            "subtreeSpecification: { base \"ou=people\" }",
            "pwdLockoutDuration: 300",
            "pwdMaxFailure: 3",
            "pwdMustChange: TRUE",
            "pwdAttribute: authPassword",
            "pwdMinAge: 600",
            "pwdMaxAge: 2147483647",
            "pwdInHistory: 5",
            "pwdExpireWarning: 864000",
            "pwdGraceAuthNLimit: 3",
            "pwdFailureCountInterval: 3600",
            "pwdAllowUserChange: FALSE",
            "pwdSafeModify: TRUE");

    InternalClientConnection connection = InternalClientConnection.getRootConnection();

    AddOperation addOperation =
        connection.processAdd(
            policyEntry.getDN(),
            policyEntry.getObjectClasses(),
            policyEntry.getUserAttributes(),
            policyEntry.getOperationalAttributes());
    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(DirectoryServer.getEntry(policyEntry.getDN()));

    PasswordPolicy policy =
        (PasswordPolicy)
            DirectoryServer.getAuthenticationPolicy(DN.decode("cn=Temp Policy," + SUFFIX));
    assertNotNull(policy);

    // Check all pwp attributes for correct values.
    assertEquals(policy.getLockoutDuration(), 300);
    assertEquals(policy.getLockoutFailureCount(), 3);
    assertEquals(policy.isForceChangeOnReset(), true);
    assertTrue(policy.getPasswordAttribute().getPrimaryName().equalsIgnoreCase("authPassword"));
    assertEquals(policy.getMinPasswordAge(), 600);
    assertEquals(policy.getMaxPasswordAge(), 2147483647);
    assertEquals(policy.getPasswordHistoryCount(), 5);
    assertEquals(policy.getPasswordExpirationWarningInterval(), 864000);
    assertEquals(policy.getGraceLoginCount(), 3);
    assertEquals(policy.getLockoutFailureExpirationInterval(), 3600);
    assertEquals(policy.isAllowUserPasswordChanges(), false);
    assertEquals(policy.isPasswordChangeRequiresCurrentPassword(), true);

    /* Check the password validator attributes for correct values.
     * The default unit-test config has a single Password validator which is
     * enabled for the default password policy.
     */
    Collection<PasswordValidator<?>> validators = policy.getPasswordValidators();
    assertEquals(validators.size(), 1);
    for (PasswordValidator<?> validator : validators) {
      assertTrue(
          validator.toString().startsWith("org.opends.server.extensions.TestPasswordValidator"));
    }

    // Make sure this policy applies to the test entry
    // its supposed to target and that its the same
    // policy object as previously tested.
    Entry testEntry = DirectoryServer.getEntry(DN.decode("uid=rogasawara," + BASE));
    assertNotNull(testEntry);

    AuthenticationPolicy statePolicy = AuthenticationPolicy.forUser(testEntry, false);
    assertNotNull(statePolicy);
    assertEquals(policy, statePolicy);

    // Make sure this policy is gone and default
    // policy is in effect instead.
    TestCaseUtils.deleteEntry(policyEntry.getDN());
    statePolicy = AuthenticationPolicy.forUser(testEntry, false);
    assertNotNull(statePolicy);
    assertEquals(defaultPolicy, statePolicy);
  }