mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

neil_a_wilson
13.05.2007 d77fbdbc376a2760ea1c4a064e1544d19798202a
Add support for user-defined virtual attributes, in which the administrator may
explicitly specify the values that should be used for a virtual attribute.
This provides functionality similar to the Class of Service (CoS) facility in
the Sun Java System Directory Server.

OpenDS Issue Number: 538
3 files added
1 files modified
988 ■■■■■ changed files
opends/resource/schema/02-config.ldif 12 ●●●● patch | view | raw | blame | history
opends/src/admin/defn/org/opends/server/admin/std/UserDefinedVirtualAttributeConfiguration.xml 38 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java 195 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProviderTestCase.java 743 ●●●●● patch | view | raw | blame | history
opends/resource/schema/02-config.ldif
@@ -1153,6 +1153,9 @@
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.343 NAME 'ds-target-group-dn'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE
  X-ORIGIN 'OpenDS Directory Server' )
attributeTypes: ( 1.3.6.1.4.1.26027.1.1.344
  NAME 'ds-cfg-virtual-attribute-value' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.1
  NAME 'ds-cfg-access-control-handler' SUP top STRUCTURAL
  MUST ( cn $ ds-cfg-acl-handler-class $ ds-cfg-acl-handler-enabled )
@@ -1605,8 +1608,11 @@
  MUST ( ds-task-rebuild-base-dn $ ds-task-rebuild-index )
  MAY ( ds-task-rebuild-max-threads )
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.99
  NAME 'ds-virtual-static-group' SUP top AUXILIARY MUST ds-target-group-dn
objectClasses: ( 1.3.6.1.4.1.26027.1.2.99 NAME 'ds-virtual-static-group'
  SUP top AUXILIARY MUST ds-target-group-dn
  X-ORIGIN 'OpenDS Directory Server' )
objectClasses: ( 1.3.6.1.4.1.26027.1.2.100
  NAME 'ds-cfg-user-defined-virtual-attribute' SUP ds-cfg-virtual-attribute
  STRUCTURAL MUST ds-cfg-virtual-attribute-value
  X-ORIGIN 'OpenDS Directory Server' )
opends/src/admin/defn/org/opends/server/admin/std/UserDefinedVirtualAttributeConfiguration.xml
New file
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<adm:managed-object name="user-defined-virtual-attribute"
plural-name="user-defined-virtual-attributes"
package="org.opends.server.admin.std" extends="virtual-attribute"
xmlns:adm="http://www.opends.org/admin"
xmlns:ldap="http://www.opends.org/admin-ldap">
  <adm:synopsis>
    The
    <adm:user-friendly-name />
    is used to create virtual attributes with user-defined values in entries
    that match the criteria set in the associated virtual attribute rule.  This
    provides functionality that is similar to Class of Service (CoS) in the Sun
    Java System Directory Server.
  </adm:synopsis>
  <adm:profile name="ldap">
    <ldap:object-class>
      <ldap:oid>1.3.6.1.4.1.26027.1.2.100</ldap:oid>
      <ldap:name>ds-cfg-user-defined-virtual-attribute</ldap:name>
      <ldap:superior>ds-cfg-virtual-attribute</ldap:superior>
    </ldap:object-class>
  </adm:profile>
  <adm:property name="value" mandatory="true" multi-valued="true">
    <adm:synopsis>
      Specifies the value (or set of values) that should be included in entries
      matching the critieria in the associated virtual attribute rule.
    </adm:synopsis>
    <adm:syntax>
      <adm:string />
    </adm:syntax>
    <adm:profile name="ldap">
      <ldap:attribute>
        <ldap:oid>1.3.6.1.4.1.26027.1.1.344</ldap:oid>
        <ldap:name>ds-cfg-virtual-attribute-value</ldap:name>
      </ldap:attribute>
    </adm:profile>
  </adm:property>
</adm:managed-object>
opends/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProvider.java
New file
@@ -0,0 +1,195 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.opends.server.admin.server.ConfigurationChangeListener;
import org.opends.server.admin.std.server.UserDefinedVirtualAttributeCfg;
import org.opends.server.api.VirtualAttributeProvider;
import org.opends.server.config.ConfigException;
import org.opends.server.core.SearchOperation;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.opends.server.types.ResultCode;
import org.opends.server.types.VirtualAttributeRule;
/**
 * This class implements a virtual attribute provider that allows administrators
 * to define their own values that will be inserted into any entry that matches
 * the criteria defined in the virtual attribute rule.  This can be used to
 * provide functionality like Class of Service (CoS) in the Sun Java System
 * Directory Server.
 */
public class UserDefinedVirtualAttributeProvider
       extends VirtualAttributeProvider<UserDefinedVirtualAttributeCfg>
       implements ConfigurationChangeListener<UserDefinedVirtualAttributeCfg>
{
  // The current configuration for this virtual attribute provider.
  private UserDefinedVirtualAttributeCfg currentConfig;
  /**
   * Creates a new instance of this member virtual attribute provider.
   */
  public UserDefinedVirtualAttributeProvider()
  {
    super();
    // All initialization should be performed in the
    // initializeVirtualAttributeProvider method.
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void initializeVirtualAttributeProvider(
                            UserDefinedVirtualAttributeCfg configuration)
         throws ConfigException, InitializationException
  {
    this.currentConfig = configuration;
    configuration.addUserDefinedChangeListener(this);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void finalizeVirtualAttributeProvider()
  {
    currentConfig.removeUserDefinedChangeListener(this);
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isMultiValued()
  {
    if (currentConfig == null)
    {
      return true;
    }
    else
    {
      return (currentConfig.getValue().size() > 1);
    }
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public LinkedHashSet<AttributeValue> getValues(Entry entry,
                                                 VirtualAttributeRule rule)
  {
    AttributeType attributeType = rule.getAttributeType();
    Set<String> userDefinedValues = currentConfig.getValue();
    LinkedHashSet<AttributeValue> values =
         new LinkedHashSet<AttributeValue>(userDefinedValues.size());
    for (String valueString : userDefinedValues)
    {
      values.add(new AttributeValue(attributeType, valueString));
    }
    return values;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public boolean isSearchable(VirtualAttributeRule rule,
                              SearchOperation searchOperation)
  {
    // We will not allow searches based only on user-defined virtual attributes.
    return false;
  }
  /**
   * {@inheritDoc}
   */
  @Override()
  public void processSearch(VirtualAttributeRule rule,
                            SearchOperation searchOperation)
  {
    searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
    return;
  }
  /**
   * {@inheritDoc}
   */
  public boolean isConfigurationChangeAcceptable(
                      UserDefinedVirtualAttributeCfg configuration,
                      List<String> unacceptableReasons)
  {
    // The new configuration should always be acceptable.
    return true;
  }
  /**
   * {@inheritDoc}
   */
  public ConfigChangeResult applyConfigurationChange(
                                 UserDefinedVirtualAttributeCfg configuration)
  {
    // Just accept the new configuration as-is.
    currentConfig = configuration;
    return new ConfigChangeResult(ResultCode.SUCCESS, false);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/UserDefinedVirtualAttributeProviderTestCase.java
New file
@@ -0,0 +1,743 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2007 Sun Microsystems, Inc.
 */
package org.opends.server.extensions;
import java.util.LinkedHashSet;
import java.util.List;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.tools.LDAPModify;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DereferencePolicy;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.types.ResultCode;
import org.opends.server.types.VirtualAttributeRule;
import static org.testng.Assert.*;
/**
 * A set of test cases for the user-defined virtual attribute provider.
 */
public class UserDefinedVirtualAttributeProviderTestCase
       extends ExtensionsTestCase
{
  // The attribute type for the description attribute.
  private AttributeType descriptionType;
  // The attribute type for the ds-privilege-name attribute.
  private AttributeType privNameType;
  // The attribute type for the ds-pwp-password-policy-dn attribute.
  private AttributeType pwPolicyDNType;
  /**
   * Ensures that the Directory Server is running.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @BeforeClass()
  public void startServer()
         throws Exception
  {
    TestCaseUtils.startServer();
    descriptionType = DirectoryServer.getAttributeType("description", false);
    assertNotNull(descriptionType);
    pwPolicyDNType =
         DirectoryServer.getAttributeType("ds-pwp-password-policy-dn", false);
    assertNotNull(pwPolicyDNType);
    privNameType = DirectoryServer.getAttributeType("ds-privilege-name", false);
    assertNotNull(privNameType);
  }
  /**
   * Tests the methods which are part of the virtual group API using a
   * single-valued virtual attribute.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRuleAPISingleValued()
         throws Exception
  {
   String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
   TestCaseUtils.addEntry(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: real-overrides-virtual",
      "ds-cfg-virtual-attribute-value: single value");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                  conn.nextMessageID(), null, DN.decode(ruleDN),
                  SearchScope.BASE_OBJECT,
                  DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                  SearchFilter.createFilterFromString("(objectClass=*)"), null,
                  null);
    for (VirtualAttributeRule rule : DirectoryServer.getVirtualAttributes())
    {
      if (rule.getAttributeType().equals(descriptionType))
      {
        UserDefinedVirtualAttributeProvider provider =
             (UserDefinedVirtualAttributeProvider) rule.getProvider();
        assertFalse(provider.isMultiValued());
        assertFalse(provider.isSearchable(rule, searchOperation));
        provider.processSearch(rule, searchOperation);
        assertEquals(searchOperation.getResultCode(),
                     ResultCode.UNWILLING_TO_PERFORM);
      }
    }
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the methods which are part of the virtual group API using a
   * multi-valued virtual attribute.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRuleAPIMultiValued()
         throws Exception
  {
   String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
   TestCaseUtils.addEntry(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: real-overrides-virtual",
      "ds-cfg-virtual-attribute-value: first value",
      "ds-cfg-virtual-attribute-value: second value");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         new InternalSearchOperation(conn, conn.nextOperationID(),
                  conn.nextMessageID(), null, DN.decode(ruleDN),
                  SearchScope.BASE_OBJECT,
                  DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
                  SearchFilter.createFilterFromString("(objectClass=*)"), null,
                  null);
    for (VirtualAttributeRule rule : DirectoryServer.getVirtualAttributes())
    {
      if (rule.getAttributeType().equals(descriptionType))
      {
        UserDefinedVirtualAttributeProvider provider =
             (UserDefinedVirtualAttributeProvider) rule.getProvider();
        assertTrue(provider.isMultiValued());
        assertFalse(provider.isSearchable(rule, searchOperation));
        provider.processSearch(rule, searchOperation);
        assertEquals(searchOperation.getResultCode(),
                     ResultCode.UNWILLING_TO_PERFORM);
      }
    }
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the creation of a description virtual attribute when there is only a
   * single virtual value and no real value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSingleDescriptionOnlyVirtual()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN = "uid=test.user,o=test";
    String value  = "This is the virtual value";
    TestCaseUtils.addEntries(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: real-overrides-virtual",
      "ds-cfg-virtual-attribute-value: " + value,
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: test");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.decode(userDN), SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    List<Attribute> attrList =
         searchOperation.getSearchEntries().get(0).getAttribute(
              descriptionType);
    assertNotNull(attrList);
    assertEquals(attrList.size(), 1);
    Attribute attr = attrList.get(0);
    assertEquals(attr.getValues().size(), 1);
    assertTrue(attr.hasValue(new AttributeValue(descriptionType, value)));
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the creation of a description virtual attribute when there are
   * multiple virtual values and no real value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testMultipleDescriptionsOnlyVirtual()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN = "uid=test.user,o=test";
    String value1 = "This is the first virtual value";
    String value2 = "This is the second virtual value";
    TestCaseUtils.addEntries(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: real-overrides-virtual",
      "ds-cfg-virtual-attribute-value: " + value1,
      "ds-cfg-virtual-attribute-value: " + value2,
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: test");
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.decode(userDN), SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    List<Attribute> attrList =
         searchOperation.getSearchEntries().get(0).getAttribute(
              descriptionType);
    assertNotNull(attrList);
    assertEquals(attrList.size(), 1);
    Attribute attr = attrList.get(0);
    assertEquals(attr.getValues().size(), 2);
    assertTrue(attr.hasValue(new AttributeValue(descriptionType, value1)));
    assertTrue(attr.hasValue(new AttributeValue(descriptionType, value2)));
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the creation of a description virtual attribute when real values
   * should override virtual values and the entry has a real value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSingleDescriptionRealOverridesVirtual()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN = "uid=test.user,o=test";
    String virtualValue = "This is the virtual value";
    String realValue    = "This is the real value";
    TestCaseUtils.addEntries(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: real-overrides-virtual",
      "ds-cfg-virtual-attribute-value: " + virtualValue,
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: test",
      "description: " + realValue);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.decode(userDN), SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    List<Attribute> attrList =
         searchOperation.getSearchEntries().get(0).getAttribute(
              descriptionType);
    assertNotNull(attrList);
    assertEquals(attrList.size(), 1);
    Attribute attr = attrList.get(0);
    assertEquals(attr.getValues().size(), 1);
    assertTrue(attr.hasValue(new AttributeValue(descriptionType, realValue)));
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the creation of a description virtual attribute when virtual values
   * should override real values and the entry has a real value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSingleDescriptionVirtualOverridesReal()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN = "uid=test.user,o=test";
    String virtualValue = "This is the virtual value";
    String realValue    = "This is the real value";
    TestCaseUtils.addEntries(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: virtual-overrides-real",
      "ds-cfg-virtual-attribute-value: " + virtualValue,
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: test",
      "description: " + realValue);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.decode(userDN), SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    List<Attribute> attrList =
         searchOperation.getSearchEntries().get(0).getAttribute(
              descriptionType);
    assertNotNull(attrList);
    assertEquals(attrList.size(), 1);
    Attribute attr = attrList.get(0);
    assertEquals(attr.getValues().size(), 1);
    assertTrue(attr.hasValue(new AttributeValue(descriptionType,
                                                virtualValue)));
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests the creation of a description virtual attribute when real and virtual
   * values should be merged and the entry has a real value.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testSingleDescriptionMergeRealAndVirtual()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String ruleDN = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN = "uid=test.user,o=test";
    String virtualValue = "This is the virtual value";
    String realValue    = "This is the real value";
    TestCaseUtils.addEntries(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: description",
      "ds-cfg-virtual-attribute-conflict-behavior: merge-real-and-virtual",
      "ds-cfg-virtual-attribute-value: " + virtualValue,
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: test",
      "description: " + realValue);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(DN.decode(userDN), SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    List<Attribute> attrList =
         searchOperation.getSearchEntries().get(0).getAttribute(
              descriptionType);
    assertNotNull(attrList);
    assertEquals(attrList.size(), 2);
    LinkedHashSet<AttributeValue> allValues =
         new LinkedHashSet<AttributeValue>();
    for (Attribute a : attrList)
    {
      allValues.addAll(a.getValues());
    }
    assertTrue(allValues.contains(new AttributeValue(descriptionType,
                                                     realValue)));
    assertTrue(allValues.contains(new AttributeValue(descriptionType,
                                                     virtualValue)));
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests to ensure that the user-defined virtual attribute provider can be
   * used to grant a privilege to a user.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testVirtualPrivilege()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String policyDN = "cn=Test Policy,cn=Password Policies,cn=config";
    String ruleDN   = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN   = "uid=test.user,o=test";
    TestCaseUtils.addEntries(
      "dn: cn=Test Policy,cn=Password Policies,cn=config",
      "objectClass: top",
      "objectClass: ds-cfg-password-policy",
      "cn: Test Policy",
      "ds-cfg-password-attribute: userPassword",
      "ds-cfg-default-password-storage-scheme: SSHA",
      "ds-cfg-allow-expired-password-changes: false",
      "ds-cfg-allow-multiple-password-values: false",
      "ds-cfg-allow-pre-encoded-passwords: false",
      "ds-cfg-allow-user-password-changes: true",
      "ds-cfg-expire-passwords-without-warning: false",
      "ds-cfg-force-change-on-add: false",
      "ds-cfg-force-change-on-reset: false",
      "ds-cfg-grace-login-count: 0",
      "ds-cfg-idle-lockout-interval: 0 seconds",
      "ds-cfg-lockout-failure-count: 0",
      "ds-cfg-lockout-duration: 0 seconds",
      "ds-cfg-lockout-failure-expiration-interval: 0 seconds",
      "ds-cfg-minimum-password-age: 0 seconds",
      "ds-cfg-maximum-password-age: 0 seconds",
      "ds-cfg-maximum-password-reset-age: 0 seconds",
      "ds-cfg-password-expiration-warning-interval: 5 days",
      "ds-cfg-password-change-requires-current-password: true",
      "ds-cfg-password-validator-dn: cn=Length-Based Password Validator," +
           "cn=Password Validators,cn=config",
      "ds-cfg-require-secure-authentication: false",
      "ds-cfg-require-secure-password-changes: false",
      "ds-cfg-skip-validation-for-administrators: false",
      "",
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: true",
      "ds-cfg-virtual-attribute-type: ds-pwp-password-policy-dn",
      "ds-cfg-virtual-attribute-conflict-behavior: merge-real-and-virtual",
      "ds-cfg-virtual-attribute-value: " + policyDN,
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: test");
    String path1 = TestCaseUtils.createTempFile(
      "dn: " + userDN,
      "changetype: modify",
      "replace: userPassword",
      "userPassword: short");
    String[] args1 =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path1
    };
    assertFalse(LDAPModify.mainModify(args1, false, null, null) == 0);
    String path2 = TestCaseUtils.createTempFile(
      "dn: " + ruleDN,
      "changetype: modify",
      "replace: ds-cfg-virtual-attribute-enabled",
      "ds-cfg-virtual-attribute-enabled: false");
    String[] args2 = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path2
    };
    assertEquals(LDAPModify.mainModify(args2, false, null, null), 0);
    assertEquals(LDAPModify.mainModify(args1, false, null, null), 0);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
    deleteOperation = conn.processDelete(DN.decode(policyDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
  /**
   * Tests to ensure that the user-defined virtual attribute provider can be
   * used to apply a custom password policy for a user.  The custom password
   * policy will reject passwords shorter than six characters, whereas the
   * default policy will not.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testVirtualPasswordPolicyDN()
         throws Exception
  {
    TestCaseUtils.initializeTestBackend(true);
    String ruleDN   = "cn=User-Defined Test,cn=Virtual Attributes,cn=config";
    String userDN   = "uid=test.user,o=test";
    TestCaseUtils.addEntries(
      "dn: " + ruleDN,
      "objectClass: top",
      "objectClass: ds-cfg-virtual-attribute",
      "objectClass: ds-cfg-user-defined-virtual-attribute",
      "cn: User-Defined Test",
      "ds-cfg-virtual-attribute-class: org.opends.server.extensions." +
           "UserDefinedVirtualAttributeProvider",
      "ds-cfg-virtual-attribute-enabled: false",
      "ds-cfg-virtual-attribute-type: ds-privilege-name",
      "ds-cfg-virtual-attribute-conflict-behavior: merge-real-and-virtual",
      "ds-cfg-virtual-attribute-value: bypass-acl",
      "",
      "dn: " + userDN,
      "objectClass: top",
      "objectClass: person",
      "objectClass: organizationalPerson",
      "objectClass: inetOrgPerson",
      "uid: test.user",
      "givenName: Test",
      "sn: User",
      "cn: Test User",
      "userPassword: password");
    String path1 = TestCaseUtils.createTempFile(
      "dn: o=test",
      "changetype: modify",
      "replace: description",
      "description: foo");
    String[] args1 =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", userDN,
      "-w", "password",
      "-f", path1
    };
    assertFalse(LDAPModify.mainModify(args1, false, null, null) == 0);
    String path2 = TestCaseUtils.createTempFile(
      "dn: " + ruleDN,
      "changetype: modify",
      "replace: ds-cfg-virtual-attribute-enabled",
      "ds-cfg-virtual-attribute-enabled: true");
    String[] args2 = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path2
    };
    assertEquals(LDAPModify.mainModify(args2, false, null, null), 0);
    assertEquals(LDAPModify.mainModify(args1, false, null, null), 0);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    DeleteOperation deleteOperation = conn.processDelete(DN.decode(ruleDN));
    assertEquals(deleteOperation.getResultCode(), ResultCode.SUCCESS);
  }
}