blob: a843931e30bebd065670e8aa354ed4438989171c [file] [log] [blame]
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.ldap.server.authz;
import org.apache.ldap.common.message.LockableAttributesImpl;
import org.apache.ldap.common.message.LockableAttributeImpl;
import org.apache.ldap.common.name.LdapName;
import org.apache.ldap.common.exception.LdapNameNotFoundException;
import org.apache.ldap.common.exception.LdapNoPermissionException;
import javax.naming.NamingException;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.directory.*;
/**
* Tests whether or not authorization around search, list and lookup operations
* work properly.
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
* @version $Rev$
*/
public class SearchAuthorizationTest extends AbstractAuthorizationTest
{
/**
* Generates a set of simple organizationalUnit entries where the
* ou of the entry returned is the index of the entry in the array.
*
* @param count the number of entries to produce
* @return an array of entries with length = count
*/
private Attributes[] getTestNodes( final int count )
{
Attributes[] attributes = new Attributes[count];
for ( int ii = 0; ii < count; ii++ )
{
attributes[ii] = new LockableAttributesImpl();
Attribute oc = new LockableAttributeImpl( "objectClass" );
oc.add( "top" );
oc.add( "organizationalUnit" );
attributes[ii].put( oc );
attributes[ii].put( "ou", String.valueOf( count ) );
}
return attributes;
}
private void recursivelyAddSearchData( Name parent, Attributes[] children, final int sizeLimit, int[] count )
throws NamingException
{
Name[] childRdns = new Name[children.length];
for ( int ii = 0; ii < children.length && count[0] < sizeLimit; ii++ )
{
Name childRdn = new LdapName();
childRdn.addAll( parent );
childRdn.add( "ou=" + ii );
childRdns[ii] = childRdn;
sysRoot.createSubcontext( childRdn, children[ii] );
count[0]++;
}
if ( count[0] >= sizeLimit )
{
return;
}
for ( int ii = 0; ii < children.length && count[0] < sizeLimit; ii++ )
{
recursivelyAddSearchData( childRdns[ii], children, sizeLimit, count );
}
}
/**
* Starts creating nodes under a parent with a set number of children. First
* a single node is created under the parent. Thereafter a number of children
* determined by the branchingFactor is added. Until a sizeLimit is reached
* descendants are created this way in a breath first recursive descent.
*
* @param parent the parent under which the first node is created
* @param branchingFactor
* @param sizelimit
* @return the immediate child node created under parent which contains the subtree
* @throws NamingException
*/
private Name addSearchData( Name parent, int branchingFactor, int sizelimit ) throws NamingException
{
parent = ( Name ) parent.clone();
parent.add( "ou=tests" );
sysRoot.createSubcontext( parent, getTestNodes(1)[0] );
recursivelyAddSearchData( parent, getTestNodes( branchingFactor ), sizelimit, new int[] { 1 } );
return parent;
}
/**
* Recursively deletes all entries including the base specified.
*
* @param rdn the relative dn from ou=system of the entry to delete recursively
* @throws NamingException if there are problems deleting entries
*/
private void recursivelyDelete( Name rdn ) throws NamingException
{
NamingEnumeration results = sysRoot.search( rdn, "(objectClass=*)", new SearchControls() );
while ( results.hasMore() )
{
SearchResult result = ( SearchResult ) results.next();
Name childRdn = new LdapName( result.getName() );
childRdn.remove( 0 );
recursivelyDelete( childRdn );
}
sysRoot.destroySubcontext( rdn );
}
/**
* Performs a single level search as a specific user on newly created data and checks
* that result set count is 3. The basic (objectClass=*) filter is used.
*
* @param uid the uid RDN attribute value for the user under ou=users,ou=system
* @param password the password of the user
* @return true if the search succeeds as expected, false otherwise
* @throws NamingException if there are problems conducting the search
*/
private boolean checkCanSearchAs( String uid, String password ) throws NamingException
{
return checkCanSearchAs( uid, password, "(objectClass=*)", null, 3 );
}
/**
* Performs a single level search as a specific user on newly created data and checks
* that result set count is equal to a user specified amount. The basic
* (objectClass=*) filter is used.
*
* @param uid the uid RDN attribute value for the user under ou=users,ou=system
* @param password the password of the user
* @param resultSetSz the expected size of the results
* @return true if the search succeeds as expected, false otherwise
* @throws NamingException if there are problems conducting the search
*/
private boolean checkCanSearchAs( String uid, String password, int resultSetSz ) throws NamingException
{
return checkCanSearchAs( uid, password, "(objectClass=*)", null, resultSetSz );
}
/**
* Performs a search as a specific user on newly created data and checks
* that result set count is equal to a user specified amount. The basic
* (objectClass=*) filter is used.
*
* @param uid the uid RDN attribute value for the user under ou=users,ou=system
* @param password the password of the user
* @param resultSetSz the expected size of the results
* @return true if the search succeeds as expected, false otherwise
* @throws NamingException if there are problems conducting the search
*/
private boolean checkCanSearchAs( String uid, String password, SearchControls cons, int resultSetSz )
throws NamingException
{
return checkCanSearchAs( uid, password, "(objectClass=*)", cons, resultSetSz );
}
/**
* Performs a search as a specific user on newly created data and checks
* that result set count is equal to a user specified amount.
*
* @param uid the uid RDN attribute value for the user under ou=users,ou=system
* @param password the password of the user
* @param filter the search filter to use
* @param resultSetSz the expected size of the results
* @return true if the search succeeds as expected, false otherwise
* @throws NamingException if there are problems conducting the search
*/
private boolean checkCanSearchAs( String uid, String password, String filter,
SearchControls cons, int resultSetSz ) throws NamingException
{
if ( cons == null )
{
cons = new SearchControls();
}
Name base = addSearchData( new LdapName(), 3, 10 );
Name userDn = new LdapName( "uid="+uid+",ou=users,ou=system" );
try
{
DirContext userCtx = getContextAs( userDn, password );
NamingEnumeration list = userCtx.search( base, filter, cons );
int counter = 0;
while ( list.hasMore() )
{
list.next();
counter++;
}
return counter == resultSetSz;
}
catch ( LdapNoPermissionException e )
{
return false;
}
finally
{
recursivelyDelete( base );
}
}
/**
* Checks to see that the addSearchData() and the recursiveDelete()
* functions in this test work properly.
*
* @throws NamingException if there is a problem with the implementation of
* these utility functions
*/
public void testAddSearchData() throws NamingException
{
// Name base = addSearchData( new LdapName(), 3, 10 );
// SearchControls controls = new SearchControls();
// controls.setSearchScope( SearchControls.SUBTREE_SCOPE );
// NamingEnumeration results = sysRoot.search( base, "(objectClass=*)", controls );
// int counter = 0;
// while ( results.hasMore() )
// {
// results.next();
// counter++;
// }
//
// assertEquals( 10, counter );
// recursivelyDelete( base );
// try { sysRoot.lookup( base ); fail(); } catch ( LdapNameNotFoundException e ) {}
}
// /**
// * Checks to make sure group membership based userClass works for add operations.
// *
// * @throws javax.naming.NamingException if the test encounters an error
// */
// public void testGrantAdministrators() throws NamingException
// {
// // create the non-admin user
// createUser( "billyd", "billyd" );
//
// // try an add operation which should fail without any ACI
// assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
//
// // Gives grantAdd perm to all users in the Administrators group for
// // entries and all attribute types and values
// createAccessControlSubentry( "searchAdmin", "{ " +
// "identificationTag \"addAci\", " +
// "precedence 14, " +
// "authenticationLevel none, " +
// "itemOrUserFirst userFirst: { " +
// "userClasses { userGroup { \"cn=Administrators,ou=groups,ou=system\" } }, " +
// "userPermissions { { " +
// "protectedItems {entry, allUserAttributeTypesAndValues}, " +
// "grantsAndDenials { grantRead, grantReturnDN, grantBrowse } } } } }" );
//
// // see if we can now add that test entry which we could not before
// // add op should still fail since billd is not in the admin group
// assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
//
// // now add billyd to the Administrator group and try again
// addUserToGroup( "billyd", "Administrators" );
//
// // try an add operation which should succeed with ACI and group membership change
// assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
// }
//
//
// /**
// * Checks to make sure name based userClass works for add operations.
// *
// * @throws javax.naming.NamingException if the test encounters an error
// */
// public void testGrantAddByName() throws NamingException
// {
// // create the non-admin user
// createUser( "billyd", "billyd" );
//
// // try an add operation which should fail without any ACI
// assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
//
// // now add a subentry that enables user billyd to add an entry below ou=system
// createAccessControlSubentry( "billydAdd", "{ " +
// "identificationTag \"addAci\", " +
// "precedence 14, " +
// "authenticationLevel none, " +
// "itemOrUserFirst userFirst: { " +
// "userClasses { name { \"uid=billyd,ou=users,ou=system\" } }, " +
// "userPermissions { { " +
// "protectedItems {entry, allUserAttributeTypesAndValues}, " +
// "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
//
// // should work now that billyd is authorized by name
// assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
// }
//
//
// /**
// * Checks to make sure subtree based userClass works for add operations.
// *
// * @throws javax.naming.NamingException if the test encounters an error
// */
// public void testGrantAddBySubtree() throws NamingException
// {
// // create the non-admin user
// createUser( "billyd", "billyd" );
//
// // try an add operation which should fail without any ACI
// assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
//
// // now add a subentry that enables user billyd to add an entry below ou=system
// createAccessControlSubentry( "billyAddBySubtree", "{ " +
// "identificationTag \"addAci\", " +
// "precedence 14, " +
// "authenticationLevel none, " +
// "itemOrUserFirst userFirst: { " +
// "userClasses { subtree { { base \"ou=users,ou=system\" } } }, " +
// "userPermissions { { " +
// "protectedItems {entry, allUserAttributeTypesAndValues}, " +
// "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
//
// // should work now that billyd is authorized by the subtree userClass
// assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
// }
//
//
// /**
// * Checks to make sure <b>allUsers</b> userClass works for add operations.
// *
// * @throws javax.naming.NamingException if the test encounters an error
// */
// public void testGrantAddAllUsers() throws NamingException
// {
// // create the non-admin user
// createUser( "billyd", "billyd" );
//
// // try an add operation which should fail without any ACI
// assertFalse( checkCanSearchAs( "billyd", "billyd" ) );
//
// // now add a subentry that enables anyone to add an entry below ou=system
// createAccessControlSubentry( "anybodyAdd", "{ " +
// "identificationTag \"addAci\", " +
// "precedence 14, " +
// "authenticationLevel none, " +
// "itemOrUserFirst userFirst: { " +
// "userClasses { allUsers }, " +
// "userPermissions { { " +
// "protectedItems {entry, allUserAttributeTypesAndValues}, " +
// "grantsAndDenials { grantAdd, grantBrowse } } } } }" );
//
// // see if we can now add that test entry which we could not before
// // should work now with billyd now that all users are authorized
// assertTrue( checkCanSearchAs( "billyd", "billyd" ) );
// }
}