From commits-return-6193-apmail-directory-commits-archive=directory.apache.org@directory.apache.org Thu Sep 22 22:44:16 2005 Return-Path: Delivered-To: apmail-directory-commits-archive@www.apache.org Received: (qmail 91609 invoked from network); 22 Sep 2005 22:44:15 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 22 Sep 2005 22:44:15 -0000 Received: (qmail 28059 invoked by uid 500); 22 Sep 2005 22:44:15 -0000 Delivered-To: apmail-directory-commits-archive@directory.apache.org Received: (qmail 28010 invoked by uid 500); 22 Sep 2005 22:44:15 -0000 Mailing-List: contact commits-help@directory.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@directory.apache.org Delivered-To: mailing list commits@directory.apache.org Received: (qmail 27995 invoked by uid 99); 22 Sep 2005 22:44:14 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 22 Sep 2005 15:44:14 -0700 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Thu, 22 Sep 2005 15:44:23 -0700 Received: (qmail 91509 invoked by uid 65534); 22 Sep 2005 22:43:53 -0000 Message-ID: <20050922224353.91508.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r291027 - in /directory/apacheds/trunk/core/src: main/java/org/apache/ldap/server/schema/SchemaChecker.java main/java/org/apache/ldap/server/schema/SchemaService.java test/resources/log4j.properties Date: Thu, 22 Sep 2005 22:43:53 -0000 To: commits@directory.apache.org From: akarasulu@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: akarasulu Date: Thu Sep 22 15:43:47 2005 New Revision: 291027 URL: http://svn.apache.org/viewcvs?rev=291027&view=rev Log: moved schema checking functionality from the modify handler of the ldap p-p into the schema checking interceptor Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaChecker.java directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaService.java directory/apacheds/trunk/core/src/test/resources/log4j.properties Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaChecker.java URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaChecker.java?rev=291027&r1=291026&r2=291027&view=diff ============================================================================== --- directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaChecker.java (original) +++ directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaChecker.java Thu Sep 22 15:43:47 2005 @@ -48,7 +48,6 @@ /** the SLF4J logger for this class */ private static Logger log = LoggerFactory.getLogger( SchemaChecker.class ); - /** * Makes sure modify operations do not leave the entry without a STRUCTURAL * objectClass. At least one STRUCTURAL objectClass must be specified for Modified: directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaService.java URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaService.java?rev=291027&r1=291026&r2=291027&view=diff ============================================================================== --- directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaService.java (original) +++ directory/apacheds/trunk/core/src/main/java/org/apache/ldap/server/schema/SchemaService.java Thu Sep 22 15:43:47 2005 @@ -34,6 +34,7 @@ import org.apache.ldap.common.filter.SimpleNode; import org.apache.ldap.common.message.LockableAttributeImpl; import org.apache.ldap.common.message.LockableAttributesImpl; +import org.apache.ldap.common.message.ResultCodeEnum; import org.apache.ldap.common.name.LdapName; import org.apache.ldap.common.schema.AttributeType; import org.apache.ldap.common.schema.DITContentRule; @@ -46,6 +47,10 @@ import org.apache.ldap.common.schema.Syntax; import org.apache.ldap.common.util.SingletonEnumeration; import org.apache.ldap.common.util.DateUtils; +import org.apache.ldap.common.util.AttributeUtils; +import org.apache.ldap.common.exception.LdapSchemaViolationException; +import org.apache.ldap.common.exception.LdapInvalidAttributeIdentifierException; +import org.apache.ldap.common.exception.LdapNoSuchAttributeException; import org.apache.ldap.server.configuration.InterceptorConfiguration; import org.apache.ldap.server.enumeration.SearchResultFilteringEnumeration; import org.apache.ldap.server.enumeration.SearchResultFilter; @@ -88,12 +93,14 @@ */ private GlobalRegistries globalRegistries; - private AttributeTypeRegistry attributeRegistry; - /** * subschemaSubentry attribute's value from Root DSE */ private String subentryDn; + + /** + * The time when the server started up. + */ private String startUpTimeStamp; /** @@ -109,7 +116,6 @@ { this.nexus = factoryCfg.getPartitionNexus(); this.globalRegistries = factoryCfg.getGlobalRegistries(); - attributeRegistry = globalRegistries.getAttributeTypeRegistry(); binaryAttributeFilter = new BinaryAttributeFilter(); // stuff for dealing with subentries (garbage for now) @@ -388,15 +394,151 @@ } + /** + * Checks to see if an attribute is required by as determined from an entry's + * set of objectClass attribute values. + * + * @param attrId the attribute to test if required by a set of objectClass values + * @param objectClass the objectClass values + * @return true if the objectClass values require the attribute, false otherwise + * @throws NamingException if the attribute is not recognized + */ + private boolean isRequired( String attrId, Attribute objectClass ) throws NamingException + { + OidRegistry oidRegistry = globalRegistries.getOidRegistry(); + ObjectClassRegistry registry = globalRegistries.getObjectClassRegistry(); + + if ( ! oidRegistry.hasOid( attrId ) ) + { + return false; + } + + String attrOid = oidRegistry.getOid( attrId ); + for ( int ii = 0; ii < objectClass.size(); ii++ ) + { + ObjectClass ocSpec = registry.lookup( ( String ) objectClass.get( ii ) ); + AttributeType[] mustList = ocSpec.getMustList(); + for ( int jj = 0; jj < mustList.length; jj++ ) + { + if ( mustList[jj].getOid().equals( attrOid ) ) + { + return true; + } + } + } + + return false; + } + + + /** + * Checks to see if removing a set of attributes from an entry completely removes + * that attribute's values. If change has zero size then all attributes are + * presumed to be removed. + * + * @param change + * @param entry + * @return + * @throws NamingException + */ + private boolean isCompleteRemoval( Attribute change, Attributes entry ) throws NamingException + { + // if change size is 0 then all values are deleted then we're screwed + if ( change.size() == 0 ) + { + return true; + } + + // can't do math to figure our if all values are removed since some + // values in the modify request may not be in the entry. we need to + // remove the values from a cloned version of the attribute and see + // if nothing is left. + Attribute changedEntryAttr = entry.get( change.getID() ); + for ( int jj = 0; jj < change.size(); jj++ ) + { + changedEntryAttr.remove( change.get( jj ) ); + } + if ( changedEntryAttr.size() == 0 ) + { + return true; + } + + return false; + } + + + Attribute getResultantObjectClasses( int modOp, Attribute changes, Attribute existing ) throws NamingException + { + if ( changes == null && existing == null ) + { + return new LockableAttributeImpl( "objectClass" ); + } + + if ( changes == null ) + { + return existing; + } + + if ( existing == null && modOp == DirContext.ADD_ATTRIBUTE ) + { + return changes; + } + else if ( existing == null ) + { + return new LockableAttributeImpl( "objectClasses" ); + } + + switch( modOp ) + { + case( DirContext.ADD_ATTRIBUTE ): + return AttributeUtils.getUnion( existing, changes ); + case( DirContext.REPLACE_ATTRIBUTE ): + return ( Attribute ) changes.clone(); + case( DirContext.REMOVE_ATTRIBUTE ): + return AttributeUtils.getDifference( existing, changes ); + default: + throw new InternalError( "" ); + } + } + + public void modify( NextInterceptor next, Name name, int modOp, Attributes mods ) throws NamingException { + Attributes entry = nexus.lookup( name ); + Attribute objectClass = getResultantObjectClasses( modOp, mods.get( "objectClass"), entry.get( "objectClass" ) ); ObjectClassRegistry ocRegistry = this.globalRegistries.getObjectClassRegistry(); + AttributeTypeRegistry atRegistry = this.globalRegistries.getAttributeTypeRegistry(); + + NamingEnumeration changes = mods.getIDs(); + while ( changes.hasMore() ) + { + String id = ( String ) changes.next(); + Attribute change = mods.get( id ); + + if ( ! atRegistry.hasAttributeType( change.getID() ) && ! objectClass.contains( "extensibleObject" ) ) + { + throw new LdapInvalidAttributeIdentifierException(); + } + + if ( modOp == DirContext.REMOVE_ATTRIBUTE && entry.get( change.getID() ) == null ) + { + throw new LdapNoSuchAttributeException(); + } + + // for required attributes we need to check if all values are removed + // if so then we have a schema violation that must be thrown + if ( modOp == DirContext.REMOVE_ATTRIBUTE && + isRequired( change.getID(), objectClass ) && + isCompleteRemoval( change, entry ) ) + { + throw new LdapSchemaViolationException( ResultCodeEnum.OBJECTCLASSVIOLATION ); + } + } if ( modOp == DirContext.REMOVE_ATTRIBUTE ) { SchemaChecker.preventRdnChangeOnModifyRemove( name, modOp, mods ); - Attribute ocAttr = this.nexus.lookup( name ).get( "objectClass" ); - SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, mods, ocAttr ); + SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, mods, objectClass ); } if ( modOp == DirContext.REPLACE_ATTRIBUTE ) @@ -411,18 +553,56 @@ public void modify( NextInterceptor next, Name name, ModificationItem[] mods ) throws NamingException { + Attributes entry = nexus.lookup( name ); + + ModificationItem objectClassMod = null; + for ( int ii = 0; ii < mods.length; ii++ ) + { + if ( ( ( String ) mods[ii].getAttribute().getID() ).equalsIgnoreCase( "objectclass" ) ) + { + objectClassMod = mods[ii]; + } + } + Attribute objectClass = null; + + if ( objectClassMod == null ) + { + objectClass = entry.get( "objectClass" ); + } + else + { + objectClass = getResultantObjectClasses( objectClassMod.getModificationOp(), + objectClassMod.getAttribute(), entry.get( "objectClass" ) ); + } + ObjectClassRegistry ocRegistry = this.globalRegistries.getObjectClassRegistry(); + AttributeTypeRegistry atRegistry = this.globalRegistries.getAttributeTypeRegistry(); for ( int ii = 0; ii < mods.length; ii++ ) { int modOp = mods[ii].getModificationOp(); Attribute change = mods[ii].getAttribute(); + if ( ! atRegistry.hasAttributeType( change.getID() ) && ! objectClass.contains( "extensibleObject" ) ) + { + throw new LdapInvalidAttributeIdentifierException(); + } + + if ( modOp == DirContext.REMOVE_ATTRIBUTE && entry.get( change.getID() ) == null ) + { + throw new LdapNoSuchAttributeException(); + } + if ( modOp == DirContext.REMOVE_ATTRIBUTE ) { + // for required attributes we need to check if all values are removed + // if so then we have a schema violation that must be thrown + if ( isRequired( change.getID(), objectClass ) && isCompleteRemoval( change, entry ) ) + { + throw new LdapSchemaViolationException( ResultCodeEnum.OBJECTCLASSVIOLATION ); + } SchemaChecker.preventRdnChangeOnModifyRemove( name, modOp, change ); - Attribute ocAttr = this.nexus.lookup( name ).get( "objectClass" ); - SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, change, ocAttr ); + SchemaChecker.preventStructuralClassRemovalOnModifyRemove( ocRegistry, name, modOp, change, objectClass ); } if ( modOp == DirContext.REPLACE_ATTRIBUTE ) @@ -457,7 +637,7 @@ for ( int ii = 0; ii < binaryArray.length; ii++ ) { - AttributeType type = attributeRegistry.lookup( binaryArray[ii] ); + AttributeType type = globalRegistries.getAttributeTypeRegistry().lookup( binaryArray[ii] ); binaries.add( type ); } @@ -477,9 +657,9 @@ boolean asBinary = false; - if ( attributeRegistry.hasAttributeType( id ) ) + if ( globalRegistries.getAttributeTypeRegistry().hasAttributeType( id ) ) { - type = attributeRegistry.lookup( id ); + type = globalRegistries.getAttributeTypeRegistry().lookup( id ); } if ( type != null ) Modified: directory/apacheds/trunk/core/src/test/resources/log4j.properties URL: http://svn.apache.org/viewcvs/directory/apacheds/trunk/core/src/test/resources/log4j.properties?rev=291027&r1=291026&r2=291027&view=diff ============================================================================== --- directory/apacheds/trunk/core/src/test/resources/log4j.properties (original) +++ directory/apacheds/trunk/core/src/test/resources/log4j.properties Thu Sep 22 15:43:47 2005 @@ -1,4 +1,4 @@ -log4j.rootCategory=DEBUG, stdout +log4j.rootCategory=INFO, stdout log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout