Skip to content

dITStructureRule implementation #382

@JesseCoretta

Description

@JesseCoretta

Describe the bug

I have reason to believe the current OpenDJ implementation of DIT structure rules and/or name forms features may have a design flaw. Looking for confirmation.

I apologize in advance for how long this is. I am trying to avoid any ambiguity and to outline my thought-process clearly. I've also tried to preemptively answer any questions that might come up. Please ping me if I failed in that regard.

To Reproduce

First let's cover the schema elements involved:

## Here is a nameForm.  Note that I am allocated the 56521 OID 
## branch and the OID in question is part of a test/example range
## (for situations exactly like this). This OID is not real and should
## not be used in the real-world.
#
nameForms: ( 1.3.6.1.4.1.56521.999.8.1.7
         NAME 'commonNameForm'
         DESC 'Name Form for a commonName orgRole structure'
         OC organizationalRole
         MUST cn )
#
## This is the "super" rule. It is subordinate to no other rules and
## in this scenario, it represents the "start" of our intended rule
## "chain".
#
dITStructureRules: ( 150
         NAME 'commonNameStructureRule'
         FORM commonNameForm )
#
## This is a subordinate rule to 150. This was my first attempt
## to facilitate the subordinate component of a structure rule
## "chain".  Note the same Name Form as 150 is used. This is
## intended.
#
dITStructureRules: ( 151
         NAME 'commonNameSubStructureRule'
         FORM commonNameForm
         SUP 150 )

The above schema is loaded by OpenDJ (4.6.5) without issue, and I can see these definitions in an ldapsearch against the cn=schema naming context as one expects.

Next, we'll test the creation of a new entry at the top of the directory. Note we've used the attribute selector to focus only on values of relevance to this issue for reasons of brevity:

dn: cn=first,dc=example,dc=com
objectClass: organizationalRole
objectClass: top
governingStructureRule: 150

The LDAP Add was successful. And, we know the structure rule (dSR) was applied due to the presence of the "governingStructureRule" (gSR), with a value matching the integer index of the indicated super "dITStructureRule" above.

But, just to be sure, let's see if the root suffix itself has been inappropriately assigned the same index:

dn: dc=example,dc=com
objectClass: domain
objectClass: top
<no governingStructureRule attr value present>

Good, its not there (which is what we'd expect, given we did not reference the "domain" STRUCTURAL class in any dSR definitions). I checked this just to make sure the implementation was doing what we expected so far.

Now, we'll create a subordinate entry of the same class and RDN type below "cn=first,dc=example,dc=com". Below is what was submitted to the server. We've satisfied the core entry requirements (cn is MUST, and we have one STRUCTURAL class -- representing the rule's "namedObjectClass" as provided by the commonNameForm Name Form):

dn: cn=second,cn=first,dc=example,dc=com
objectClass: organizationalRole
objectClass: top

An error was received:

[LDAP result code 65 - objectClassViolation] Entry cn=second,cn=first,dc=example,dc=com is invalid
according to the server schema because there is no DIT structure rule that applies to that entry, but
there is a DIT structure rule for the parent entry cn=first,dc=example,dc=com

The same error ensues if we REMOVE rule 151, and try to rely solely on 150. I have no idea WHY that would work, but it didn't hurt to try.

So now we know what happens with only one (1) rule in action, AND we know that duplicating the rules into a logical sup/sub "chain" also does not work ...

... but what happens if we use another "namedObjectClass" within a new Name Form in subordinate fashion -- such as an organizationalPerson -- which will allow the same RDN to be used, but with another STRUCTURAL class in play?

Lets find out:

First, create an additional name form with an alternative STRUCTURAL class, and make 'cn' a MUST clause member as before. This allows us to use the same RDN easily (other classes could have been used, but organizationalPerson is widely available, so ...).

nameForms: ( 1.3.6.1.4.1.56521.999.8.1.8
          NAME 'altCommonNameForm'
          DESC 'alternative commonName name form'
          OC organizationalPerson
          MUST cn )

Next, replace our original rule 151 with the following. Note we are now using an entirely separate Name Form, but the rule remains subordinate to 150.

dITStructureRules: ( 151
          NAME 'commonNameSubStructureRule'
          FORM altCommonNameForm
          SUP 150 ) 

Restart OpenDS (no errors shown). Again, the new incarnation of these definitions show up properly when doing an ldapsearch.

Retry the LDAP Add with the slightly-adjusted "cn=second" entry, now using the appropriate STRUCTURAL class mandated by the new "altCommonNameForm" name form:

dn: cn=second,cn=first,dc=example,dc=com
objectClass: organizationalPerson
objectClass: top
sn: any value just to satisfy MUST

LDAP Add worked! Same location, same RDN ... but this time with a different structuralObjectClass (and, more to the point, a different "namedObjectClass"). I think that is the key right there.

And, to top it all off, we see the correct gSR during an LDAP search of the new entry, meaning the system recognized rule compliance:

governingStructureRule: 151

Now, I only did this "class swap" technique to prove my point ... I do not believe this is an acceptable solution, nor do I believe it was the intention of those who implemented this specification within OpenDJ.

But for reasons of due diligence, before I submitted this ticket, I did another brush-up on X.501, the ITU-T X-series document in which the bulk of DIT structure rule mechanics are described. I also scanned others, such as X.511 and X.519, as the topic of these structure rules is mentioned there as well.

Unless I missed something, I see nothing in the specification that would preclude the use of a "chained" structure rules as demonstrated here, which just happen to involve the same "namedObjectClass". Moreover, I can't perceive a reason why such a limitation would, or should, exist. Then again, that could just be due to a limited imagination on my part.

I am able to reproduce this behavior with other class combinations, both official RFC-based classes, as well as custom ones.

Can anyone confirm? Assuming I'm correct in that the specification does NOT forbid this strategy, I'm hoping this is a simple bug to fix within OpenDJ.

Expected behavior

One should be able to use a chain of sub/sup dSRs that share the same "namedObjectClass" -- whether or not the same Name Form is involved. For example, to summarize in full, the following SHOULD be allowed:

dn: cn=first,dc=example,dc=com
objectClass: organizationalRole
objectClass: top
governingStructureRule: 150

dn: cn=second,cn=first,dc=example,dc=com
objectClass: organizationalRole
objectClass: top
governingStructureRule: 151

## ... other entries ...
##
## Note: Chain of governed entries SHOULD be able to continue
## vertically as needed, provided a sufficiently long dSR chain has
## been defined in the directory schema.  For the purposes of this
## issue, we only used two (2).

... and, conversely, the act of swapping arbitrary classes within the chain should NOT be required as demonstrated earlier.

Setup/Environment

  • OS: Ubuntu 22
  • Clients: ldap-utils and Apache DS GUI app
    • Though it shouldn't matter, after each restart of OpenDJ, I not only disconnect Apache DS but I also reload its schema cache at the start of the next session
  • Version: OpenDJ 4.6.5 (via your Releases page), although other versions may be impacted
    • The setup of OpenDJ was very vanilla -- other that the above schema definitions, no other configuration changes were made

Thank you for your time, and also a big THANK YOU for supporting dSRs in the first place -- this is a useful yet seldom-implemented feature 😃

Jesse

EDIT: grammar

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions