From bbbf0365ea2bf1fccafb6a57c2a7c0243a59b325 Mon Sep 17 00:00:00 2001
From: ludovicp <ludovicp@localhost>
Date: Wed, 18 Aug 2010 13:14:11 +0000
Subject: [PATCH] Add support for inherited collective attributes. This extension to collective attributes brings the feature on par with DSEE CoS.

---
 opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java             |   91 +++++++
 opends/src/server/org/opends/server/types/Entry.java                                                      |  241 ++++++++++++++++++++
 opends/resource/schema/00-core.ldif                                                                       |   32 ++
 opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java |    3 
 opends/src/server/org/opends/server/core/SubentryManager.java                                             |    4 
 opends/src/server/org/opends/server/types/SubEntry.java                                                   |  275 ++++++++++++++++++++++
 opends/src/server/org/opends/server/util/ServerConstants.java                                             |   60 +++++
 7 files changed, 699 insertions(+), 7 deletions(-)

diff --git a/opends/resource/schema/00-core.ldif b/opends/resource/schema/00-core.ldif
index 3853601..1c485de 100644
--- a/opends/resource/schema/00-core.ldif
+++ b/opends/resource/schema/00-core.ldif
@@ -399,6 +399,21 @@
 attributeTypes: ( 1.3.6.1.4.1.26027.1.1.606
   NAME 'collectiveConflictBehavior' SYNTAX 1.3.6.1.4.1.26027.1.3.6
   SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.621 NAME 'inheritFromDNAttribute'
+  EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.622 NAME 'inheritFromBaseDN'
+  EQUALITY distinguishedNameMatch 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.623 NAME 'inheritFromRDNType'
+  EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.624 NAME 'inheritFromRDNAttribute'
+  EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+  SINGLE-VALUE X-ORIGIN 'OpenDS Directory Server' )
+attributeTypes: ( 1.3.6.1.4.1.26027.1.1.625 NAME 'inheritAttribute'
+  EQUALITY objectIdentifierMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.38
+  X-ORIGIN 'OpenDS Directory Server' )
 attributeTypes: ( 2.5.4.7.1 NAME 'c-l' SUP l COLLECTIVE X-ORIGIN 'RFC 3671' )
 attributeTypes: ( 2.5.4.8.1 NAME 'c-st' SUP st COLLECTIVE X-ORIGIN 'RFC 3671' )
 attributeTypes: ( 2.5.4.9.1 NAME 'c-street' SUP street COLLECTIVE
@@ -659,6 +674,23 @@
 objectClasses: ( 2.5.17.2 NAME 'collectiveAttributeSubentry'
   DESC 'LDAP Collective Attributes Subentry class' AUXILIARY
   X-ORIGIN 'RFC 3671' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.238 NAME
+  'inheritedCollectiveAttributeSubentry'
+  DESC 'Inherited Collective Attributes Subentry class' SUP subentry
+  STRUCTURAL MUST inheritAttribute MAY collectiveConflictBehavior
+  X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.239 NAME
+  'inheritedFromDNCollectiveAttributeSubentry'
+  DESC 'Inherited from DN Collective Attributes Subentry class'
+  SUP inheritedCollectiveAttributeSubentry STRUCTURAL
+  MUST ( inheritFromDNAttribute )
+  X-ORIGIN 'OpenDS Directory Server' )
+objectClasses: ( 1.3.6.1.4.1.26027.1.2.240 NAME
+  'inheritedFromRDNCollectiveAttributeSubentry'
+  DESC 'Inherited from RDN Collective Attributes Subentry class'
+  SUP inheritedCollectiveAttributeSubentry STRUCTURAL
+  MUST ( inheritFromRDNAttribute $ inheritFromRDNType $ inheritFromBaseDN )
+  X-ORIGIN 'OpenDS Directory Server' )
 objectClasses: ( 2.16.840.1.113730.3.2.33 NAME 'groupOfURLs'
   DESC 'Sun-defined objectclass' SUP top STRUCTURAL MUST ( cn )
   MAY ( memberURL $ businessCategory $ description $ o $ ou $ owner $ seeAlso )
diff --git a/opends/src/server/org/opends/server/core/SubentryManager.java b/opends/src/server/org/opends/server/core/SubentryManager.java
index 385c9a0..adfa86d 100644
--- a/opends/src/server/org/opends/server/core/SubentryManager.java
+++ b/opends/src/server/org/opends/server/core/SubentryManager.java
@@ -214,7 +214,7 @@
     lock.writeLock().lock();
     try
     {
-      if (subEntry.isCollective())
+      if (subEntry.isCollective() || subEntry.isInheritedCollective())
       {
         subList = dn2CollectiveSubEntry.get(subDN);
       }
@@ -225,7 +225,7 @@
       if (subList == null)
       {
         subList = new ArrayList<SubEntry>();
-        if (subEntry.isCollective())
+        if (subEntry.isCollective() || subEntry.isInheritedCollective())
         {
           dn2CollectiveSubEntry.put(subDN, subList);
         }
diff --git a/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java b/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java
index a76b094..482c47c 100644
--- a/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java
+++ b/opends/src/server/org/opends/server/extensions/CollectiveAttributeSubentriesVirtualAttributeProvider.java
@@ -115,7 +115,8 @@
               DirectoryServer.getAttributeType("2.5.4.49");
       for (SubEntry subentry : subentries)
       {
-        if (subentry.isCollective())
+        if (subentry.isCollective() ||
+            subentry.isInheritedCollective())
         {
           DN subentryDN = subentry.getDN();
           AttributeValue value = AttributeValues.create(
diff --git a/opends/src/server/org/opends/server/types/Entry.java b/opends/src/server/org/opends/server/types/Entry.java
index 228572c..fd37d33 100644
--- a/opends/src/server/org/opends/server/types/Entry.java
+++ b/opends/src/server/org/opends/server/types/Entry.java
@@ -3424,6 +3424,152 @@
 
 
   /**
+   * Indicates whether the entry meets the criteria to consider it an
+   * inherited collective attributes subentry (i.e., it contains
+   * the "inheritedCollectiveAttributeSubentry" objectclass).
+   *
+   * @return  <CODE>true</CODE> if this entry meets the criteria to
+   *          consider it an inherited collective attributes
+   *          subentry, or <CODE>false</CODE> if not.
+   */
+  public boolean isInheritedCollectiveAttributeSubentry()
+  {
+    ObjectClass inheritedCollectiveAttributeSubentryOC =
+         DirectoryServer.getObjectClass(
+         OC_INHERITED_COLLECTIVE_ATTR_SUBENTRY_LC);
+    if (inheritedCollectiveAttributeSubentryOC == null)
+    {
+      // This should not happen -- The server doesn't have
+      // an inheritedCollectiveAttributeSubentry object
+      // class defined.
+      if (debugEnabled())
+      {
+        TRACER.debugWarning(
+            "No %s objectclass is defined in the server schema.",
+                     OC_INHERITED_COLLECTIVE_ATTR_SUBENTRY);
+      }
+
+      for (String ocName : objectClasses.values())
+      {
+        if (ocName.equalsIgnoreCase(
+                OC_INHERITED_COLLECTIVE_ATTR_SUBENTRY))
+        {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+
+    // Make the determination based on whether this entry
+    // has the inheritedCollectiveAttributeSubentry
+    // objectclass.
+    return objectClasses.containsKey(
+            inheritedCollectiveAttributeSubentryOC);
+  }
+
+
+
+  /**
+   * Indicates whether the entry meets the criteria to consider it
+   * an inherited from DN collective attributes subentry (i.e., it
+   * contains the "inheritedFromDNCollectiveAttributeSubentry"
+   * objectclass).
+   *
+   * @return  <CODE>true</CODE> if this entry meets the criteria to
+   *          consider it an inherited from DN collective attributes
+   *          subentry, or <CODE>false</CODE> if not.
+   */
+  public boolean isInheritedFromDNCollectiveAttributeSubentry()
+  {
+    ObjectClass inheritedFromDNCollectiveAttributeSubentryOC =
+         DirectoryServer.getObjectClass(
+         OC_INHERITED_FROM_DN_COLLECTIVE_ATTR_SUBENTRY_LC);
+    if (inheritedFromDNCollectiveAttributeSubentryOC == null)
+    {
+      // This should not happen -- The server doesn't have
+      // an inheritedFromDNCollectiveAttributeSubentry
+      // object class defined.
+      if (debugEnabled())
+      {
+        TRACER.debugWarning(
+            "No %s objectclass is defined in the server schema.",
+            OC_INHERITED_FROM_DN_COLLECTIVE_ATTR_SUBENTRY);
+      }
+
+      for (String ocName : objectClasses.values())
+      {
+        if (ocName.equalsIgnoreCase(
+                OC_INHERITED_FROM_DN_COLLECTIVE_ATTR_SUBENTRY))
+        {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+
+    // Make the determination based on whether this entry
+    // has the inheritedCollectiveAttributeSubentry
+    // objectclass.
+    return objectClasses.containsKey(
+            inheritedFromDNCollectiveAttributeSubentryOC);
+  }
+
+
+
+  /**
+   * Indicates whether the entry meets the criteria to consider it
+   * an inherited from RDN collective attributes subentry (i.e.,
+   * it contains the "inheritedFromRDNCollectiveAttributeSubentry"
+   * objectclass).
+   *
+   * @return  <CODE>true</CODE> if this entry meets the criteria to
+   *          consider it an inherited from RDN collective attributes
+   *          subentry, or <CODE>false</CODE> if not.
+   */
+  public boolean isInheritedFromRDNCollectiveAttributeSubentry()
+  {
+    ObjectClass inheritedFromRDNCollectiveAttributeSubentryOC =
+         DirectoryServer.getObjectClass(
+         OC_INHERITED_FROM_RDN_COLLECTIVE_ATTR_SUBENTRY_LC);
+    if (inheritedFromRDNCollectiveAttributeSubentryOC == null)
+    {
+      // This should not happen -- The server doesn't have
+      // an inheritedFromRDNCollectiveAttributeSubentry
+      // object class defined.
+      if (debugEnabled())
+      {
+        TRACER.debugWarning(
+            "No %s objectclass is defined in the server schema.",
+            OC_INHERITED_FROM_RDN_COLLECTIVE_ATTR_SUBENTRY);
+      }
+
+      for (String ocName : objectClasses.values())
+      {
+        if (ocName.equalsIgnoreCase(
+                OC_INHERITED_FROM_RDN_COLLECTIVE_ATTR_SUBENTRY))
+        {
+          return true;
+        }
+      }
+
+      return false;
+    }
+
+
+    // Make the determination based on whether this entry
+    // has the inheritedCollectiveAttributeSubentry
+    // objectclass.
+    return objectClasses.containsKey(
+            inheritedFromRDNCollectiveAttributeSubentryOC);
+  }
+
+
+
+  /**
    * Indicates whether the entry meets the criteria to consider it a
    * LDAP password policy subentry (i.e., it contains the "pwdPolicy"
    * objectclass of LDAP Password Policy Internet-Draft).
@@ -3543,8 +3689,83 @@
     // Process collective attributes.
     for (SubEntry subEntry : collectiveAttrSubentries)
     {
-      if (subEntry.isCollective())
+      if (subEntry.isCollective() || subEntry.isInheritedCollective())
       {
+        Entry inheritFromEntry = null;
+        if (subEntry.isInheritedCollective())
+        {
+          if (subEntry.isInheritedFromDNCollective() &&
+              hasAttribute(subEntry.getInheritFromDNType()))
+          {
+            try
+            {
+              DN inheritFromDN = null;
+              for (Attribute attr : getAttribute(
+                   subEntry.getInheritFromDNType()))
+              {
+                for (AttributeValue value : attr)
+                {
+                  inheritFromDN = DN.decode(
+                          value.getNormalizedValue());
+                  break;
+                }
+              }
+              if (inheritFromDN == null)
+              {
+                continue;
+              }
+
+              // TODO : ACI check; needs re-factoring to happen.
+              inheritFromEntry = DirectoryServer.getEntry(
+                    inheritFromDN);
+            }
+            catch (DirectoryException de)
+            {
+              if (debugEnabled())
+              {
+                TRACER.debugCaught(DebugLogLevel.ERROR, de);
+              }
+            }
+          }
+          else if (subEntry.isInheritedFromRDNCollective() &&
+                   hasAttribute(subEntry.getInheritFromRDNAttrType()))
+          {
+            DN inheritFromDN = subEntry.getInheritFromBaseDN();
+            if (inheritFromDN != null)
+            {
+              try
+              {
+                for (Attribute attr : getAttribute(
+                   subEntry.getInheritFromRDNAttrType()))
+                {
+                  inheritFromDN = subEntry.getInheritFromBaseDN();
+                  for (AttributeValue value : attr)
+                  {
+                    inheritFromDN = inheritFromDN.concat(
+                        RDN.create(subEntry.getInheritFromRDNType(),
+                        value));
+                    break;
+                  }
+                }
+
+                // TODO : ACI check; needs re-factoring to happen.
+                inheritFromEntry = DirectoryServer.getEntry(
+                        inheritFromDN);
+              }
+              catch (DirectoryException de)
+              {
+                if (debugEnabled())
+                {
+                  TRACER.debugCaught(DebugLogLevel.ERROR, de);
+                }
+              }
+            }
+            else
+            {
+              continue;
+            }
+          }
+        }
         List<Attribute> collectiveAttrList =
                 subEntry.getCollectiveAttributes();
         for (Attribute collectiveAttr : collectiveAttrList)
@@ -3556,6 +3777,24 @@
           {
             continue;
           }
+          if (subEntry.isInheritedCollective())
+          {
+            if (inheritFromEntry != null)
+            {
+              collectiveAttr = inheritFromEntry.getExactAttribute(
+                      collectiveAttr.getAttributeType(),
+                      collectiveAttr.getOptions());
+              if ((collectiveAttr == null) ||
+                  (collectiveAttr.isEmpty()))
+              {
+                continue;
+              }
+            }
+            else
+            {
+              continue;
+            }
+          }
           List<Attribute> attrList =
                   userAttributes.get(attributeType);
           if ((attrList == null) || attrList.isEmpty())
diff --git a/opends/src/server/org/opends/server/types/SubEntry.java b/opends/src/server/org/opends/server/types/SubEntry.java
index 8cbfe5a..4d13e2e 100644
--- a/opends/src/server/org/opends/server/types/SubEntry.java
+++ b/opends/src/server/org/opends/server/types/SubEntry.java
@@ -110,6 +110,41 @@
   public static final String ATTR_COLLECTIVE_CONFLICT_BEHAVIOR =
           "collectiveconflictbehavior";
 
+  /**
+   * The name of the "inheritFromDNAttribute" attribute type,
+   * formatted in all lowercase characters.
+   */
+  public static final String ATTR_INHERIT_COLLECTIVE_FROM_DN =
+          "inheritfromdnattribute";
+
+  /**
+   * The name of the "inheritFromRDNAttribute" attribute type,
+   * formatted in all lowercase characters.
+   */
+  public static final String ATTR_INHERIT_COLLECTIVE_FROM_RDN =
+          "inheritfromrdnattribute";
+
+  /**
+   * The name of the "inheritFromRDNType" attribute type,
+   * formatted in all lowercase characters.
+   */
+  public static final String ATTR_INHERIT_COLLECTIVE_FROM_RDN_TYPE =
+          "inheritfromrdntype";
+
+  /**
+   * The name of the "inheritFromBaseDN" attribute type,
+   * formatted in all lowercase characters.
+   */
+  public static final String ATTR_INHERIT_COLLECTIVE_FROM_BASE =
+          "inheritfrombasedn";
+
+  /**
+   * The name of the "inheritAttribute" attribute type,
+   * formatted in all lowercase characters.
+   */
+  public static final String ATTR_INHERIT_COLLECTIVE_ATTR =
+          "inheritattribute";
+
   // Attribute option to mark attributes collective.
   private static final String ATTR_OPTION_COLLECTIVE =
           "collective";
@@ -123,6 +158,33 @@
   // Collective subentry flag.
   private boolean isCollective = false;
 
+  // Inherited collective subentry flag.
+  private boolean isInheritedCollective = false;
+
+  // Inherited collective from DN subentry flag.
+  private boolean isInheritedFromDNCollective = false;
+
+  // Inherited collective from RDN subentry flag.
+  private boolean isInheritedFromRDNCollective = false;
+
+  // Inherited collective DN attribute type.
+  private AttributeType inheritFromDNType = null;
+
+  // Inherited collective RDN attribute type.
+  private AttributeType inheritFromRDNAttrType = null;
+
+  // Inherited collective RDN type attribute type.
+  private AttributeType inheritFromRDNType = null;
+
+  // Inherited collective RDN attribute value.
+  private AttributeValue inheritFromRDNAttrValue = null;
+
+  // Inherited collective from DN value.
+  private AttributeValue inheritFromDNAttrValue = null;
+
+  // Inherited collective from base DN.
+  private DN inheritFromBaseDN = null;
+
   // Collective attributes.
   private List<Attribute> collectiveAttributes;
 
@@ -228,6 +290,18 @@
     // Determine if this subentry is collective attribute subentry.
     this.isCollective = entry.isCollectiveAttributeSubentry();
 
+    // Determine if this subentry is inherited collective
+    // attribute subentry and if so what kind.
+    this.isInheritedCollective =
+            entry.isInheritedCollectiveAttributeSubentry();
+    if (this.isInheritedCollective)
+    {
+      this.isInheritedFromDNCollective =
+              entry.isInheritedFromDNCollectiveAttributeSubentry();
+      this.isInheritedFromRDNCollective =
+              entry.isInheritedFromRDNCollectiveAttributeSubentry();
+    }
+
     // Process collective attributes.
     this.collectiveAttributes = new ArrayList<Attribute>();
     if (this.isCollective)
@@ -257,7 +331,102 @@
           this.collectiveAttributes.add(collectiveAttr);
         }
       }
-      // Conflict behavior.
+    }
+
+    // Process inherited collective attributes.
+    if (this.isInheritedCollective)
+    {
+      if (this.isInheritedFromDNCollective)
+      {
+        List<Attribute> attrList = entry.getAttribute(
+                ATTR_INHERIT_COLLECTIVE_FROM_DN);
+        if ((attrList != null) && !attrList.isEmpty())
+        {
+          for (Attribute attr : attrList)
+          {
+            for (AttributeValue value : attr)
+            {
+              this.inheritFromDNType =
+                      DirectoryServer.getAttributeType(
+                      value.toString().toLowerCase(),
+                      true);
+              this.inheritFromDNAttrValue = value;
+              break;
+            }
+          }
+        }
+      }
+
+      if (this.isInheritedFromRDNCollective)
+      {
+        List<Attribute> attrList = entry.getAttribute(
+                ATTR_INHERIT_COLLECTIVE_FROM_RDN);
+        if ((attrList != null) && !attrList.isEmpty())
+        {
+          for (Attribute attr : attrList)
+          {
+            for (AttributeValue value : attr)
+            {
+              this.inheritFromRDNAttrType =
+                      DirectoryServer.getAttributeType(
+                      value.toString().toLowerCase(),
+                      true);
+              this.inheritFromRDNAttrValue = value;
+              break;
+            }
+          }
+        }
+        attrList = entry.getAttribute(
+                ATTR_INHERIT_COLLECTIVE_FROM_RDN_TYPE);
+        if ((attrList != null) && !attrList.isEmpty())
+        {
+          for (Attribute attr : attrList)
+          {
+            for (AttributeValue value : attr)
+            {
+              this.inheritFromRDNType =
+                      DirectoryServer.getAttributeType(
+                      value.toString().toLowerCase(),
+                      true);
+              break;
+            }
+          }
+        }
+        attrList = entry.getAttribute(
+                ATTR_INHERIT_COLLECTIVE_FROM_BASE);
+        if ((attrList != null) && !attrList.isEmpty())
+        {
+          for (Attribute attr : attrList)
+          {
+            for (AttributeValue value : attr)
+            {
+              this.inheritFromBaseDN =
+                      DN.decode(value.getNormalizedValue());
+              break;
+            }
+          }
+        }
+      }
+
+      List<Attribute> attrList = entry.getAttribute(
+              ATTR_INHERIT_COLLECTIVE_ATTR);
+      if ((attrList != null) && !attrList.isEmpty())
+      {
+        for (Attribute attr : attrList)
+        {
+          for (AttributeValue value : attr)
+          {
+            this.collectiveAttributes.add(
+                    Attributes.empty(
+                    value.toString()));
+          }
+        }
+      }
+    }
+
+    // Establish collective attribute conflict behavior.
+    if (this.isCollective || this.isInheritedCollective)
+    {
       List<Attribute> attrList = entry.getAttribute(
               ATTR_COLLECTIVE_CONFLICT_BEHAVIOR);
       if ((attrList != null) && !attrList.isEmpty())
@@ -312,6 +481,110 @@
   }
 
   /**
+   * Indicates whether or not this subentry is
+   * an inherited collective attribute subentry.
+   * @return <code>true</code> if inherited
+   *         collective, <code>false</code>
+   *         otherwise.
+   */
+  public boolean isInheritedCollective()
+  {
+    return this.isInheritedCollective;
+  }
+
+  /**
+   * Indicates whether or not this subentry is
+   * an inherited from DN collective attribute
+   * subentry.
+   * @return <code>true</code> if inherited
+   *         from DN collective,
+   *         <code>false</code> otherwise.
+   */
+  public boolean isInheritedFromDNCollective()
+  {
+    return this.isInheritedFromDNCollective;
+  }
+
+  /**
+   * Indicates whether or not this subentry is
+   * an inherited from RDN collective attribute
+   * subentry.
+   * @return <code>true</code> if inherited
+   *         from RDN collective,
+   *         <code>false</code> otherwise.
+   */
+  public boolean isInheritedFromRDNCollective()
+  {
+    return this.isInheritedFromRDNCollective;
+  }
+
+  /**
+   * Getter to retrieve inheritFromDNAttribute type
+   * for inherited collective attribute subentry.
+   * @return Type of inheritFromDNAttribute or,
+   *         <code>null</code> if there is none.
+   */
+  public AttributeType getInheritFromDNType()
+  {
+    return this.inheritFromDNType;
+  }
+
+  /**
+   * Getter to retrieve inheritFromRDNAttribute type
+   * for inherited collective attribute subentry.
+   * @return Type of inheritFromRDNAttribute or,
+   *         <code>null</code> if there is none.
+   */
+  public AttributeType getInheritFromRDNAttrType()
+  {
+    return this.inheritFromRDNAttrType;
+  }
+
+  /**
+   * Getter to retrieve inheritFromRDNAttribute value
+   * for inherited collective attribute subentry.
+   * @return AttributeValue of inheritFromRDNAttribute
+   *         or, <code>null</code> if there is none.
+   */
+  public AttributeValue getInheritFromRDNAttrValue()
+  {
+    return this.inheritFromRDNAttrValue;
+  }
+
+  /**
+   * Getter to retrieve RDN type of inheritFromRDNType
+   * for inherited collective attribute subentry.
+   * @return RDN Type of inheritFromRDNAttribute or,
+   *         <code>null</code> if there is none.
+   */
+  public AttributeType getInheritFromRDNType()
+  {
+    return this.inheritFromRDNType;
+  }
+
+  /**
+   * Getter to retrieve inheritFromDNAttribute value
+   * for inherited collective attribute subentry.
+   * @return AttributeValue of inheritFromDNAttribute
+   *         or, <code>null</code> if there is none.
+   */
+  public AttributeValue getInheritFromDNAttrValue()
+  {
+    return this.inheritFromDNAttrValue;
+  }
+
+  /**
+   * Getter to retrieve inheritFromBaseDN DN
+   * for inherited collective attribute subentry.
+   * @return DN of inheritFromBaseDN or,
+   *         <code>null</code> if there is none.
+   */
+  public DN getInheritFromBaseDN()
+  {
+    return this.inheritFromBaseDN;
+  }
+
+  /**
    * Getter for subentry subtree specification.
    * @return subtree specification for this subentry.
    */
diff --git a/opends/src/server/org/opends/server/util/ServerConstants.java b/opends/src/server/org/opends/server/util/ServerConstants.java
index 4b325ea..7532f88 100644
--- a/opends/src/server/org/opends/server/util/ServerConstants.java
+++ b/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -929,6 +929,66 @@
 
 
   /**
+   * The name of the "inheritedCollectiveAttributeSubentry" objectclass
+   * (which is a special type of objectclass that makes a kind of COS
+   * template subentry), formatted in camel case.
+   */
+  public static final String OC_INHERITED_COLLECTIVE_ATTR_SUBENTRY =
+          "inheritedCollectiveAttributeSubentry";
+
+
+
+  /**
+   * The name of the "inheritedCollectiveAttributeSubentry" objectclass
+   * (which is a special type of objectclass that makes a kind of COS
+   * template subentry), formatted in all lowercase.
+   */
+  public static final String OC_INHERITED_COLLECTIVE_ATTR_SUBENTRY_LC =
+          "inheritedcollectiveattributesubentry";
+
+
+
+  /**
+   * The name of the "inheritedFromDNCollectiveAttributeSubentry" objectclass
+   * (which is a special type of objectclass that makes a kind of indirect
+   * COS template subentry), formatted in camel case.
+   */
+  public static final String OC_INHERITED_FROM_DN_COLLECTIVE_ATTR_SUBENTRY =
+          "inheritedFromDNCollectiveAttributeSubentry";
+
+
+
+  /**
+   * The name of the "inheritedFromDNCollectiveAttributeSubentry" objectclass
+   * (which is a special type of objectclass that makes a kind of indirect
+   * COS template subentry), formatted in all lowercase.
+   */
+  public static final String OC_INHERITED_FROM_DN_COLLECTIVE_ATTR_SUBENTRY_LC =
+          "inheritedfromdncollectiveattributesubentry";
+
+
+
+  /**
+   * The name of the "inheritedFromRDNCollectiveAttributeSubentry" objectclass
+   * (which is a special type of objectclass that makes a kind of classic
+   * COS template subentry), formatted in camel case.
+   */
+  public static final String OC_INHERITED_FROM_RDN_COLLECTIVE_ATTR_SUBENTRY =
+          "inheritedFromRDNCollectiveAttributeSubentry";
+
+
+
+  /**
+   * The name of the "inheritedFromRDNCollectiveAttributeSubentry" objectclass
+   * (which is a special type of objectclass that makes a kind of classic
+   * COS template subentry), formatted in all lowercase.
+   */
+  public static final String OC_INHERITED_FROM_RDN_COLLECTIVE_ATTR_SUBENTRY_LC =
+          "inheritedfromrdncollectiveattributesubentry";
+
+
+
+  /**
    * The name of the LDAP Password Policy Internet-Draft
    * "pwdPolicy" objectclass, formatted in camel case.
    */
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
index 84f1c4c..8fb68e4 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/core/SubentryManagerTestCase.java
@@ -160,6 +160,75 @@
   }
 
   @Test
+  public void testInheritedCollectiveAttributes() throws Exception
+  {
+    InternalClientConnection connection =
+         InternalClientConnection.getRootConnection();
+
+    // Add test inherited from DN collective subentry.
+    Entry collectiveDNInheritedSubentry = TestCaseUtils.makeEntry(
+         "dn: cn=Inherited From DN Collective Subentry," + SUFFIX,
+         "objectClass: top",
+         "objectclass: subentry",
+         "objectClass: inheritedCollectiveAttributeSubentry",
+         "objectClass: inheritedFromDNCollectiveAttributeSubentry",
+         "inheritFromDNAttribute: manager",
+         "inheritAttribute: postalAddress",
+         "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}",
+         "cn: Inherited From DN Collective Subentry");
+    AddOperation addOperation =
+         connection.processAdd(collectiveDNInheritedSubentry.getDN(),
+             collectiveDNInheritedSubentry.getObjectClasses(),
+             collectiveDNInheritedSubentry.getUserAttributes(),
+             collectiveDNInheritedSubentry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(DirectoryServer.getEntry(
+            collectiveDNInheritedSubentry.getDN()));
+
+    // Add test inherited from RDN collective subentry.
+    Entry collectiveRDNInheritedSubentry = TestCaseUtils.makeEntry(
+         "dn: cn=Inherited From RDN Collective Subentry," + SUFFIX,
+         "objectClass: top",
+         "objectclass: subentry",
+         "objectClass: inheritedCollectiveAttributeSubentry",
+         "objectClass: inheritedFromRDNCollectiveAttributeSubentry",
+         "inheritFromBaseDN: " + BASE,
+         "inheritFromRDNAttribute: title",
+         "inheritFromRDNType: cn",
+         "inheritAttribute: telephoneNumber",
+         "subtreeSpecification: {base \"ou=Test SubEntry Manager\"}",
+         "cn: Inherited From RDN Collective Subentry");
+    addOperation =
+         connection.processAdd(collectiveRDNInheritedSubentry.getDN(),
+             collectiveRDNInheritedSubentry.getObjectClasses(),
+             collectiveRDNInheritedSubentry.getUserAttributes(),
+             collectiveRDNInheritedSubentry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(DirectoryServer.getEntry(
+            collectiveRDNInheritedSubentry.getDN()));
+
+    // Test Inherited Collective Attributes on test entry.
+    Entry entry = DirectoryServer.getEntry(testEntry.getDN());
+    AttributeType attrType = DirectoryServer.getAttributeType(
+            "postaladdress");
+    assertTrue(entry.hasAttribute(attrType));
+    assertTrue(entry.hasValue(attrType, null,
+            AttributeValues.create(attrType,
+            "Sub City, Collective Street, AK 47")));
+
+    attrType = DirectoryServer.getAttributeType(
+            "telephonenumber");
+    assertTrue(entry.hasAttribute(attrType));
+    assertTrue(entry.hasValue(attrType, null,
+            AttributeValues.create(attrType,
+            "+1 999 999 9999")));
+
+    // Cleanup.
+    TestCaseUtils.deleteEntry(collectiveRDNInheritedSubentry.getDN());
+    TestCaseUtils.deleteEntry(collectiveDNInheritedSubentry.getDN());
+  }
+
+  @Test
   public void testCollectiveAttributeConflict() throws Exception
   {
     InternalClientConnection conn =
@@ -564,6 +633,23 @@
       assertNotNull(DirectoryServer.getEntry(baseEntry.getDN()));
     }
 
+    // Add role entry.
+    Entry roleEntry = TestCaseUtils.makeEntry(
+         "dn: cn=Sales," + BASE,
+         "objectclass: top",
+         "objectclass: organizationalRole",
+         "postalAddress: Sub City, Collective Street, AK 47",
+         "telephoneNumber: +1 999 999 9999",
+         "cn: Sales"
+    );
+    AddOperation addOperation =
+         connection.processAdd(roleEntry.getDN(),
+                               roleEntry.getObjectClasses(),
+                               roleEntry.getUserAttributes(),
+                               roleEntry.getOperationalAttributes());
+    assertEquals(addOperation.getResultCode(), ResultCode.SUCCESS);
+    assertNotNull(DirectoryServer.getEntry(roleEntry.getDN()));
+
     // Add test entry.
     testEntry = TestCaseUtils.makeEntry(
          "dn: uid=rogasawara," + BASE,
@@ -577,9 +663,10 @@
          "givenname: Rodney",
          "sn: Ogasawara",
          "cn: Rodney Ogasawara",
-         "title: Sales, Director"
+         "manager: cn=Sales," + BASE,
+         "title: Sales"
     );
-    AddOperation addOperation =
+    addOperation =
          connection.processAdd(testEntry.getDN(),
                                testEntry.getObjectClasses(),
                                testEntry.getUserAttributes(),

--
Gitblit v1.10.0