| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| */ |
| package org.apache.directory.server; |
| |
| |
| import java.util.Hashtable; |
| |
| import javax.naming.NamingEnumeration; |
| import javax.naming.NamingException; |
| import javax.naming.directory.Attribute; |
| import javax.naming.directory.Attributes; |
| import javax.naming.directory.DirContext; |
| import javax.naming.directory.InvalidAttributeIdentifierException; |
| import javax.naming.directory.InvalidAttributeValueException; |
| import javax.naming.directory.ModificationItem; |
| import javax.naming.directory.NoSuchAttributeException; |
| import javax.naming.directory.SchemaViolationException; |
| import javax.naming.directory.SearchControls; |
| import javax.naming.directory.SearchResult; |
| import javax.naming.ldap.InitialLdapContext; |
| import javax.naming.ldap.LdapContext; |
| |
| import org.apache.directory.server.unit.AbstractServerTest; |
| import org.apache.directory.shared.ldap.message.AttributeImpl; |
| import org.apache.directory.shared.ldap.message.AttributesImpl; |
| import org.apache.directory.shared.ldap.message.ModificationItemImpl; |
| |
| |
| /** |
| * Testcase with different modify operations on a person entry. Each includes a |
| * single removal op only. |
| * |
| * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> |
| * @version $Rev$ |
| */ |
| public class ModifyRemoveTest extends AbstractServerTest |
| { |
| |
| private LdapContext ctx = null; |
| |
| private static final String RDN = "cn=Tori Amos"; |
| |
| |
| /** |
| * Creation of required attributes of a person entry. |
| */ |
| protected Attributes getPersonAttributes( String sn, String cn ) |
| { |
| Attributes attributes = new AttributesImpl(); |
| Attribute attribute = new AttributeImpl( "objectClass" ); |
| attribute.add( "top" ); |
| attribute.add( "person" ); |
| attributes.put( attribute ); |
| attributes.put( "cn", cn ); |
| attributes.put( "sn", sn ); |
| |
| return attributes; |
| } |
| |
| |
| /** |
| * Creation of required attributes of an inetOrgPerson entry. |
| */ |
| protected Attributes getInetOrgPersonAttributes( String sn, String cn ) |
| { |
| Attributes attrs = new AttributesImpl(); |
| Attribute ocls = new AttributeImpl( "objectClass" ); |
| ocls.add( "top" ); |
| ocls.add( "person" ); |
| ocls.add( "organizationalPerson" ); |
| ocls.add( "inetOrgPerson" ); |
| attrs.put( ocls ); |
| attrs.put( "cn", cn ); |
| attrs.put( "sn", sn ); |
| |
| return attrs; |
| } |
| |
| |
| /** |
| * Create context and a person entry. |
| */ |
| public void setUp() throws Exception |
| { |
| super.setUp(); |
| |
| Hashtable<String,Object> env = new Hashtable<String,Object>(); |
| env.put( "java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory" ); |
| env.put( "java.naming.provider.url", "ldap://localhost:" + port + "/ou=system" ); |
| env.put( "java.naming.security.principal", "uid=admin,ou=system" ); |
| env.put( "java.naming.security.credentials", "secret" ); |
| env.put( "java.naming.security.authentication", "simple" ); |
| |
| ctx = new InitialLdapContext( env, null ); |
| assertNotNull( ctx ); |
| |
| // Create a person with description |
| Attributes attributes = this.getPersonAttributes( "Amos", "Tori Amos" ); |
| attributes.put( "description", "an American singer-songwriter" ); |
| ctx.createSubcontext( RDN, attributes ); |
| } |
| |
| |
| /** |
| * Remove person entry and close context. |
| */ |
| public void tearDown() throws Exception |
| { |
| ctx.unbind( RDN ); |
| ctx.close(); |
| ctx = null; |
| super.tearDown(); |
| } |
| |
| |
| /** |
| * Just a little test to check wether opening the connection and creation of |
| * the person succeeds succeeds. |
| * |
| * @throws NamingException |
| */ |
| public void testSetUpTearDown() throws NamingException |
| { |
| assertNotNull( ctx ); |
| DirContext tori = ( DirContext ) ctx.lookup( RDN ); |
| assertNotNull( tori ); |
| } |
| |
| |
| /** |
| * Remove an attribute which does not exist in an attribute making sure |
| * it does not remove other values in that attribute. Tests if the |
| * following JIRA issue is still valid: |
| * |
| * https://issues.apache.org/jira/browse/DIRSERVER-1149 |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveAttemptWithoutChange() throws NamingException |
| { |
| // Get the attributes and check the contents |
| Attributes tori = ctx.getAttributes( RDN ); |
| assertNotNull( tori.get( "objectClass" ) ); |
| assertNotNull( tori.get( "cn" ) ); |
| assertEquals( 1, tori.get( "cn" ).size() ); |
| assertEquals( "Tori Amos", tori.get( "cn" ).get() ); |
| assertNotNull( tori.get( "sn" ) ); |
| |
| // Test an add operation first |
| ModificationItem mod = new ModificationItem( DirContext.ADD_ATTRIBUTE, new AttributeImpl( "cn", "foo" ) ); |
| ctx.modifyAttributes( RDN, new ModificationItem[] { mod } ); |
| tori = ctx.getAttributes( RDN ); |
| assertNotNull( tori.get( "objectClass" ) ); |
| assertNotNull( tori.get( "cn" ) ); |
| assertEquals( 2, tori.get( "cn" ).size() ); |
| assertEquals( "Tori Amos", tori.get( "cn" ).get( 0 ) ); |
| assertEquals( "foo", tori.get( "cn" ).get( 1 ) ); |
| assertNotNull( tori.get( "sn" ) ); |
| |
| // Now test remove of value ( bar ) that does not exist in cn |
| mod = new ModificationItem( DirContext.REMOVE_ATTRIBUTE, new AttributeImpl( "cn", "bar" ) ); |
| ctx.modifyAttributes( RDN, new ModificationItem[] { mod } ); |
| tori = ctx.getAttributes( RDN ); |
| assertNotNull( tori.get( "objectClass" ) ); |
| assertNotNull( tori.get( "cn" ) ); |
| assertEquals( 2, tori.get( "cn" ).size() ); |
| assertEquals( "Tori Amos", tori.get( "cn" ).get( 0 ) ); |
| assertEquals( "foo", tori.get( "cn" ).get( 1 ) ); |
| assertNotNull( tori.get( "sn" ) ); |
| } |
| |
| |
| /** |
| * Remove an attribute, which is not required. |
| * |
| * Expected result: After successful deletion, attribute is not present in |
| * entry. |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveNotRequiredAttribute() throws NamingException |
| { |
| // Remove description Attribute |
| Attribute attr = new AttributeImpl( "description" ); |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( attr ); |
| ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| |
| // Verify, that attribute is deleted |
| attrs = ctx.getAttributes( RDN ); |
| attr = attrs.get( "description" ); |
| assertNull( attr ); |
| } |
| |
| |
| /** |
| * Remove two not required attributes. |
| * |
| * Expected result: After successful deletion, both attributes ar not |
| * present in entry. |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveTwoNotRequiredAttributes() throws NamingException |
| { |
| // add telephoneNumber to entry |
| Attributes tn = new AttributesImpl( "telephoneNumber", "12345678" ); |
| ctx.modifyAttributes( RDN, DirContext.ADD_ATTRIBUTE, tn ); |
| |
| // Remove description and telephoneNumber to Attribute |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( new AttributeImpl( "description" ) ); |
| attrs.put( new AttributeImpl( "telephoneNumber" ) ); |
| ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| |
| // Verify, that attributes are deleted |
| attrs = ctx.getAttributes( RDN ); |
| assertNull( attrs.get( "description" ) ); |
| assertNull( attrs.get( "telephoneNumber" ) ); |
| assertNotNull( attrs.get( "cn" ) ); |
| assertNotNull( attrs.get( "sn" ) ); |
| } |
| |
| |
| /** |
| * Remove a required attribute. The sn attribute of the person entry is used |
| * here. |
| * |
| * Expected Result: Deletion fails with NamingException (Schema Violation). |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveRequiredAttribute() throws NamingException |
| { |
| // Remove sn attribute |
| Attribute attr = new AttributeImpl( "sn" ); |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| fail( "Deletion of required attribute should fail." ); |
| } |
| catch ( SchemaViolationException e ) |
| { |
| // expected behaviour |
| } |
| } |
| |
| |
| /** |
| * Remove a required attribute from RDN. |
| * |
| * Expected Result: Deletion fails with SchemaViolationException. |
| * |
| * @throws NamingException |
| */ |
| public void testRemovePartOfRdn() throws NamingException |
| { |
| // Remove sn attribute |
| Attribute attr = new AttributeImpl( "cn" ); |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| fail( "Deletion of RDN attribute should fail." ); |
| } |
| catch ( SchemaViolationException e ) |
| { |
| // expected behaviour |
| } |
| } |
| |
| |
| /** |
| * Remove a not required attribute from RDN. |
| * |
| * Expected Result: Deletion fails with SchemaViolationException. |
| * |
| * @throws NamingException |
| */ |
| public void testRemovePartOfRdnNotRequired() throws NamingException |
| { |
| // Change RDN to another attribute |
| String newRdn = "description=an American singer-songwriter"; |
| ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" ); |
| ctx.rename( RDN, newRdn ); |
| |
| // Remove description, which is now RDN attribute |
| Attribute attr = new AttributeImpl( "description" ); |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( newRdn, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| fail( "Deletion of RDN attribute should fail." ); |
| } |
| catch ( SchemaViolationException e ) |
| { |
| // expected behaviour |
| } |
| |
| // Change RDN back to original |
| ctx.addToEnvironment( "java.naming.ldap.deleteRDN", "false" ); |
| ctx.rename( newRdn, RDN ); |
| } |
| |
| |
| /** |
| * Remove a an attribute which is not present on the entry, but in the |
| * schema. |
| * |
| * Expected result: Deletion fails with NoSuchAttributeException |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveAttributeNotPresent() throws NamingException |
| { |
| // Remove telephoneNumber Attribute |
| Attribute attr = new AttributeImpl( "telephoneNumber" ); |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| fail( "Deletion of attribute, which is not present in the entry, should fail." ); |
| } |
| catch ( NoSuchAttributeException e ) |
| { |
| // expected behaviour |
| } |
| } |
| |
| |
| /** |
| * Remove a an attribute which is not present in the schema. |
| * |
| * Expected result: Deletion fails with NoSuchAttributeException |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveAttributeNotValid() throws NamingException |
| { |
| // Remove phantasy attribute |
| Attribute attr = new AttributeImpl( "XXX" ); |
| Attributes attrs = new AttributesImpl(); |
| attrs.put( attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( RDN, DirContext.REMOVE_ATTRIBUTE, attrs ); |
| fail( "Deletion of an invalid attribute should fail." ); |
| } |
| catch ( NoSuchAttributeException e ) |
| { |
| // expected behaviour |
| } |
| catch ( InvalidAttributeIdentifierException e ) |
| { |
| // expected behaviour |
| } |
| } |
| |
| |
| /** |
| * Create a person entry and try to remove an attribute value |
| * |
| * @throws NamingException |
| */ |
| public void testReplaceNonExistingAttribute() throws NamingException |
| { |
| // Create an entry |
| Attributes attrs = getInetOrgPersonAttributes( "Bush", "Kate Bush" ); |
| attrs.put( "givenname", "Kate" ); |
| String rdn = "cn=Kate Bush"; |
| ctx.createSubcontext( rdn, attrs ); |
| |
| // replace attribute givenName with empty value (=> deletion) |
| Attribute attr = new AttributeImpl( "givenname" ); |
| ModificationItemImpl item = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr ); |
| ctx.modifyAttributes( rdn, new ModificationItemImpl[] { item } ); |
| |
| SearchControls sctls = new SearchControls(); |
| sctls.setSearchScope( SearchControls.ONELEVEL_SCOPE ); |
| String filter = "(cn=Kate Bush)"; |
| String base = ""; |
| NamingEnumeration enm = ctx.search( base, filter, sctls ); |
| if ( enm.hasMore() ) |
| { |
| SearchResult sr = ( SearchResult ) enm.next(); |
| attrs = sr.getAttributes(); |
| Attribute cn = sr.getAttributes().get( "cn" ); |
| assertNotNull( cn ); |
| assertTrue( cn.contains( "Kate Bush" ) ); |
| |
| // Check whether attribute has been removed |
| Attribute givenName = sr.getAttributes().get( "givenname" ); |
| assertNull( givenName ); |
| } |
| else |
| { |
| fail( "entry not found" ); |
| } |
| |
| ctx.destroySubcontext( rdn ); |
| } |
| |
| |
| /** |
| * Create a person entry and try to remove an attribute value from the RDN |
| * by Replacement |
| * |
| * @throws NamingException |
| */ |
| public void testReplaceRdnByEmptyValueAttribute() throws NamingException |
| { |
| |
| // Create an entry |
| Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" ); |
| String rdn = "cn=Kate Bush"; |
| ctx.createSubcontext( rdn, attrs ); |
| |
| // replace attribute cn with empty value (=> deletion) |
| Attribute attr = new AttributeImpl( "cn" ); |
| ModificationItemImpl item = new ModificationItemImpl( DirContext.REPLACE_ATTRIBUTE, attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( rdn, new ModificationItemImpl[] |
| { item } ); |
| fail( "modify should fail" ); |
| } |
| catch ( SchemaViolationException e ) |
| { |
| // Expected behaviour |
| } |
| |
| ctx.destroySubcontext( rdn ); |
| } |
| |
| |
| /** |
| * Create a person entry and try to remove an attribute from the RDN |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveRdnAttribute() throws NamingException |
| { |
| |
| // Create an entry |
| Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" ); |
| String rdn = "cn=Kate Bush"; |
| ctx.createSubcontext( rdn, attrs ); |
| |
| // replace attribute cn with empty value (=> deletion) |
| Attribute attr = new AttributeImpl( "cn" ); |
| ModificationItemImpl item = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( rdn, new ModificationItemImpl[] |
| { item } ); |
| fail( "modify should fail" ); |
| } |
| catch ( SchemaViolationException e ) |
| { |
| // Expected behaviour |
| } |
| |
| ctx.destroySubcontext( rdn ); |
| } |
| |
| |
| /** |
| * Create a person entry and try to remove an attribute from the RDN |
| * |
| * @throws NamingException |
| */ |
| public void testRemoveRdnAttributeValue() throws NamingException |
| { |
| |
| // Create an entry |
| Attributes attrs = getPersonAttributes( "Bush", "Kate Bush" ); |
| String rdn = "cn=Kate Bush"; |
| ctx.createSubcontext( rdn, attrs ); |
| |
| // replace attribute cn with empty value (=> deletion) |
| Attribute attr = new AttributeImpl( "cn", "Kate Bush" ); |
| ModificationItemImpl item = new ModificationItemImpl( DirContext.REMOVE_ATTRIBUTE, attr ); |
| |
| try |
| { |
| ctx.modifyAttributes( rdn, new ModificationItemImpl[] |
| { item } ); |
| fail( "modify should fail" ); |
| } |
| catch ( SchemaViolationException e ) |
| { |
| // Expected behaviour |
| } |
| |
| ctx.destroySubcontext( rdn ); |
| } |
| |
| /** |
| * Create a person entry and try to remove objectClass attribute |
| * |
| * @throws NamingException |
| */ |
| public void testDeleteOclAttrWithTopPersonOrganizationalpersonInetorgperson() throws NamingException { |
| |
| // Create an entry |
| Attributes attrs = getInetOrgPersonAttributes("Bush", "Kate Bush"); |
| String rdn = "cn=Kate Bush"; |
| ctx.createSubcontext(rdn, attrs); |
| |
| ModificationItemImpl delModOp = new ModificationItemImpl(DirContext.REMOVE_ATTRIBUTE, new AttributeImpl("objectclass", "")); |
| |
| try { |
| ctx.modifyAttributes(rdn, new ModificationItemImpl[] { delModOp }); |
| fail("deletion of objectclass should fail"); |
| } catch (SchemaViolationException e) { |
| // expected |
| } catch (NoSuchAttributeException e) { |
| // expected |
| } catch (InvalidAttributeValueException e) { |
| // expected |
| } catch ( Exception e ) { |
| e.printStackTrace(); |
| } |
| |
| ctx.destroySubcontext(rdn); |
| } |
| |
| /** |
| * Create a person entry and try to remove objectClass attribute. A variant |
| * which works. |
| * |
| * @throws NamingException |
| */ |
| public void testDeleteOclAttrWithTopPersonOrganizationalpersonInetorgpersonVariant() throws NamingException { |
| |
| // Create an entry |
| Attributes attrs = getInetOrgPersonAttributes("Bush", "Kate Bush"); |
| String rdn = "cn=Kate Bush"; |
| ctx.createSubcontext(rdn, attrs); |
| |
| ModificationItemImpl delModOp = new ModificationItemImpl(DirContext.REMOVE_ATTRIBUTE, new AttributeImpl("objectclass")); |
| |
| try { |
| ctx.modifyAttributes(rdn, new ModificationItemImpl[] { delModOp }); |
| fail("deletion of objectclass should fail"); |
| } catch (SchemaViolationException e) { |
| // expected |
| } |
| |
| ctx.destroySubcontext(rdn); |
| } |
| } |