/* ======================================================================
The Bodington System Software License, Version 1.0

Copyright (c) 2001 The University of Leeds.  All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1.  Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2.  Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3.  The end-user documentation included with the redistribution, if any,
must include the following acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.

4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.

5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
EVENT SHALL THE UNIVERSITY OF LEEDS OR ITS CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
=========================================================

This software was originally created by the University of Leeds and may contain voluntary
contributions from others.  For more information on the Bodington Open Source Project, please
see http://bodington.org/

====================================================================== */

package org.bodington.server.realm;

import org.apache.log4j.Logger;

import org.bodington.server.*;
import org.bodington.server.resources.*;
import org.bodington.sqldatabase.*;
import org.bodington.database.*;

import java.security.*;
import java.security.acl.*;
import java.util.*;

/**
 * An implementation of java.security.acl.Acl.  This
 * class manages a list of AclEntry objects.  At present
 * entries must refer to instances of Group and
 * individual entries of type User aren't allowed.
 * Each entry associates a group with a set of
 * permissions.
 *
 * @author Jon Maber
 * @version 1.0
 */
public class Acl extends org.bodington.sqldatabase.SqlPersistentObject implements java.security.acl.Acl
	{
    
    private static Logger log = Logger.getLogger(Acl.class);
    
	private PrimaryKey acl_id;
	private String name;

    /* @todo remove owners_group variable, use owners_acl_entry to get group: */
	private Group owners_group;

    /**
     * Added owners_acl_entry. (Can be used to get owners group, remove existing owners_group variable?)
     * Added adhoc_group variable. <p>
     * <i>(WebLearn modification (variables modified/added): 04/05/2004 Colin Tatham)</i>
     */
        private AclEntry owners_acl_entry;
	private Group adhoc_group;

	//Hashtable entries;						// hashed against acl_entry_id
	private Hashtable positive_group_entries;			//hashed against group_id
	private Hashtable negative_group_entries;			//hashed against group_id
	private Vector positive_individual_entries;   // individual entries not implemented yet
	private Vector negative_individual_entries;   // individual entries not implemented yet


    private Vector entries_to_delete;

    private static PrimaryKey system_acl_id;

    /**
     * Not yet in use - may refer to a "system" acl that will enforce
     * system wide access rights.
     */
    private static Acl system_acl;



	/**
	 * Finds an Acl from store.
	 *
	 * @param key The primary key of the requested Acl.
	 * @return The Acl or null.
	 * @exception org.bodington.server.BuildingServerException Thrown in various circumstances.
	 */
	public static Acl findAcl( PrimaryKey key )
	    throws BuildingServerException
	    {
	    return (Acl)findPersistentObject( key, "org.bodington.server.realm.Acl" );
	    }

	/**
	 * Find an Acl according to a search expression.
	 *
	 * @param where An SQL like where clause.
	 * @return An Acl or null.
	 * @exception org.bodington.server.BuildingServerException Thrown in various circumstances.
	 */
	public static Acl findAcl( String where )
	    throws BuildingServerException
	    {
	    return (Acl)findPersistentObject( where, "org.bodington.server.realm.Acl" );
	    }

	/**
	 * Finds a set of Acls.
	 *
	 * @param where An SQL like where clause.
	 * @return An enumeration of Acls found.
	 * @exception org.bodington.server.BuildingServerException
	 */
	public static Enumeration findAcls( String where )
	    throws BuildingServerException
	    {
	    return findPersistentObjects( where, "org.bodington.server.realm.Acl" );
	    }

    /**
     * Used when loading an Acl from store - sets the primary key.
     *
     * @param key The primarykey value to set.
     */
    public void setPrimaryKey( PrimaryKey key )
    	{
    	setAclId( key );
	    }

    /**
     * Retrieves the priamry key.
     *
     * @return The key or null if this is a newly instantiated Acl.
     */
    public PrimaryKey getPrimaryKey()
	    {
        return getAclId();
	    }

	/**
	 * This contructor is used when an Acl is loaded
	 * from storage.  It just initialises Hashtables
	 * and a Vector.
	 */
	public Acl()
		{
		positive_group_entries = new Hashtable();
		negative_group_entries = new Hashtable();
		entries_to_delete = new Vector();
        }

	/**
	 * This constructor is used to create a completely
	 * new Acl and store it. It sets the ACL on the resource and saves
     * both the ACL and the Resource objects. This is all very crazy for a 
     * constructor and don't as me why.
	 * @param creator The calling user.
	 * @param resource A reference to the resource associated with this ACL.
	 * @exception org.bodington.server.BuildingServerException Thrown in various circumstances.
	 */
	public Acl( java.security.Principal creator, Resource resource )
		throws BuildingServerException
		{
		//this contructor is use to set up an entirely new instance
		// whereas the previous contructor is used before loading an
		// instance from storage.
		positive_group_entries = new Hashtable();
		negative_group_entries = new Hashtable();
		entries_to_delete = new Vector();

		if ( !(creator instanceof org.bodington.server.realm.User ) )
			throw new BuildingServerException( "Acl owner must be type org.bodington.server.realm.User" );

		setName( "a_new_acl" );
		save();

        resource.setAclId( getAclId() );
        resource.save();

		String owners_group_name = "localgroup." + resource.getResourceId() + ".owners";
        owners_group=new Group();
        owners_group.setResourceId( resource.getResourceId() );
		owners_group.setName( owners_group_name );
		owners_group.setDescription( "People who own this resource. " +
				"Each resource has a separate owners group."       );
		owners_group.save();
		owners_group.addMember( creator );
		owners_group.save();

        String adhoc_group_name = "localgroup." + resource.getResourceId() + ".adhoc";
        adhoc_group = new Group();
        adhoc_group.setResourceId( resource.getResourceId() );
		adhoc_group.setName( adhoc_group_name );
		adhoc_group.setDescription(
				"An adhoc group who can be granted access to this resource. " +
				"Each resource has a separate adhoc group."       );
		adhoc_group.save();
/*
 * Weblearn modification: created class variable, renamed existing local variable
 *  to owners_acl_entry : 26/07/2004 Colin Tatham <br />
 */

        owners_acl_entry = new AclEntry();
        owners_acl_entry.setPrincipal( owners_group );
        Enumeration enumeration = Permission.permissions();
        Permission permission;
        owners_acl_entry.addPermission( Permission.SEE );
        owners_acl_entry.addPermission( Permission.VIEW );
        owners_acl_entry.addPermission( Permission.EDIT );
        owners_acl_entry.addPermission( Permission.POST );
        owners_acl_entry.addPermission( Permission.CREATE );
        owners_acl_entry.addPermission( Permission.UPLOAD );
        owners_acl_entry.addPermission( Permission.MANAGE );
        //owner automatically gets all permissions except administer and sysadmin
        /*
        while ( enumeration.hasMoreElements() )
            {
            permission = (Permission)enumeration.nextElement();
            Logger.getLogger( "org.bodington" ).info( permission.toString() );
            if ( permission.equals( Permission.ADMINISTER ) ||
                 permission.equals( Permission.SYSADMIN ) )
                continue;
            Logger.getLogger( "org.bodington" ).info( "Adding " + permission.toString() );
            entry.addPermission( permission );
            }
        */
        //use private version to bypass access check.
        //must bypass because before the entry is added noone
        //owns the ACL.  Any user could call this constructor
        //and they must be allowed to add themselves to the
        //access rights as an owner.
        privateAddEntry( owners_acl_entry );
		save();
		}

    /**
     * Speaks for itself.
     *
     * @param key The key to use for this property.
     */
    public void setAclId( PrimaryKey key )
    	{
    	acl_id =  key;
    	setUnsaved();
	    }

    /**
     * Gets a property.
     *
     * @return The acl_id of this Acl.
     */
    public PrimaryKey getAclId()
	    {
        return acl_id;
	    }

	/**
	 * Gets a property.
	 *
	 * @return The name of the Acl.
	 */
	public String getName()
		{
		return name;
		}

	/**
	 * Sets a property.
	 *
	 * @param x The name to set.
	 */
	public void setName( String x )
		{
    	setUnsaved();
		name=x;
		}

	/**
	 * Loads the owners group and loads the AclEntries. <p>
	 * <i>(WebLearn modification (adhoc_group and owners_acl_entry set): 04/05/2004 Colin Tatham)</i>
	 * @exception org.bodington.server.BuildingServerException Thrown in various circumstances.
	 */
	public synchronized void loadReferencedObjects()
		throws BuildingServerException
		{
		AclEntry entry;
		Group group;
		String group_name;

		positive_group_entries = new Hashtable();
		negative_group_entries = new Hashtable();


		Enumeration enumeration = AclEntry.findAclEntries( "acl_id = " + getAclId().toString() );
		if ( enumeration == null )
			return;

		owners_group = null;
                owners_acl_entry = null;
                adhoc_group = null;

                while ( enumeration.hasMoreElements() )
                {
                  entry = (AclEntry)enumeration.nextElement();
                  group = entry.getGroup();
                  if ( group.isLocalGroup() )
                  {
                    if ( group.isOwnersGroup() )
                    {
                      owners_group = group;
                      owners_acl_entry = entry;
                    }
                    else if ( group.isAdhocGroup())
                      adhoc_group = group;
                  }
                  //entries.put( entry.getPrimaryKey(), entry );
                  if ( entry.isNegative() )
                    negative_group_entries.put( entry.getGroupId(), entry );
                  else
                    positive_group_entries.put( entry.getGroupId(), entry );
                    //addMember( member.getUser() );
                }

		return;
		}
	/**
	 * Saves the properties of the acl and also saves all the
	 * entries.
	 *
	 * @exception org.bodington.server.BuildingServerException Thrown in various circumstances.
	 */

	public synchronized void save()
		throws BuildingServerException
		{
		super.save();
		AclEntry entry;
		Enumeration enumeration;
		for ( enumeration=negative_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = (AclEntry)enumeration.nextElement();
			entry.setAclId( getAclId() );
			entry.save();
			}
		for ( enumeration=positive_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = (AclEntry)enumeration.nextElement();
			entry.setAclId( getAclId() );
			entry.save();
			}
		for ( int i=0; i<entries_to_delete.size(); i++ )
		    {
		    entry = (AclEntry)entries_to_delete.elementAt( i );
		    entry.delete();
		    }
		entries_to_delete.removeAllElements();
		}


	/**
	 * One owner can add someone else.
	 *
	 * @param caller The user who is adding.
	 * @param newowner The user to be added.
	 * @return If the owner was added returns true.
	 * @exception java.security.acl.NotOwnerException Thrown if the caller parameter is not an owner.
	 */
	public synchronized boolean addOwner( java.security.Principal caller, java.security.Principal newowner)
		throws NotOwnerException
		{
		//if ( !isOwner( caller ) )
		//	throw new NotOwnerException();
		if ( !(newowner instanceof User ) )
			throw new NotOwnerException();
		if ( isOwner( newowner ) )
			return false;
	    if ( owners_group==null )
			throw new NotOwnerException();
		owners_group.addMember( newowner );

		return true;
		}


	/**
	 * Deletes an owner of the ACL.
	 *
	 * @param caller The person calling.
	 * @param newowner The owner to remove.
	 * @return If successful returns true.
	 * @exception java.security.acl.NotOwnerException Thrown if caller is not an owner.
	 * @exception java.security.acl.LastOwnerException Thrown if the owner requested to delete is the last owner.
	 */
	public synchronized boolean deleteOwner( java.security.Principal caller, java.security.Principal newowner )
		throws NotOwnerException, LastOwnerException
		{
		//if ( !isOwner( caller ) )
		//	throw new NotOwnerException();
		if ( !(newowner instanceof User ) )
			throw new NotOwnerException();
		if ( !isOwner( newowner ) )
			return false;
	    if ( owners_group==null )
			throw new NotOwnerException();

		owners_group.removeMemberChecked( newowner );
		return true;
		}

	/**
	 * Checks if user is an owner.  In this implementation it checks
	 * the manage access right of the acl itself to check this.  This is
	 * because the owners group should always be an entry in the acl.
	 *
	 * @param possibleowner The user to test.
	 * @return If the user is an owner returns true.
	 */
	//anyone with manage access in this acl counts as an owner of the acl.
	//i.e. not just members of the owners group.
	public synchronized boolean isOwner( java.security.Principal possibleowner )
		{
		return checkPermission( possibleowner, Permission.forName( "manage" ) );
		}


	/**
	 * Adds an entry - checks user who is adding from BuildingContext
	 * of this thread.
	 *
	 * @param newentry The entry to add
	 * @return If added returns true.
	 * @exception java.security.acl.NotOwnerException Thrown if caller is not an owner.
	 */
	public synchronized boolean addEntry( java.security.acl.AclEntry newentry )
		throws NotOwnerException
		{
		BuildingContext context=BuildingContext.getContext();
		return addEntry( context.getUser(), newentry );
		}


	/**
	 * Required by java.security.acl.Acl interface.  Adds an entry to
	 * the ACL.
	 *
	 * @param caller The user who is adding the entry.
	 * @param newentry The entry to add.
	 * @return If successful returns true.
	 * @exception java.security.acl.NotOwnerException Thrown if caller is not an owner of the acl.
	 */
	public synchronized boolean addEntry( java.security.Principal caller,  java.security.acl.AclEntry newentry)
		throws NotOwnerException
		{
		//if ( !isOwner( caller ) )
		//	throw new NotOwnerException();
		if ( !(newentry instanceof org.bodington.server.realm.AclEntry) )
			throw new NotOwnerException();
	    return privateAddEntry( newentry );
	    }

	/**
	 * Like addEntry but without security check.  Needed when creating
	 * a new Acl.
	 *
	 * @param newentry The entry to add.
	 * @return If successful returns true.
	 */
	private synchronized boolean privateAddEntry( java.security.acl.AclEntry newentry)
		{
		org.bodington.server.realm.AclEntry entry = (org.bodington.server.realm.AclEntry)newentry;
		AclEntry oldentry;
		if ( entry.isNegative() )
			oldentry = (AclEntry)negative_group_entries.get( entry.getGroupId() );
		else
			oldentry = (AclEntry)positive_group_entries.get( entry.getGroupId() );

		if ( oldentry!=null )
			{
			if ( oldentry.toString().equals( entry.toString() ) )
				return false;
			if ( oldentry.isNegative() )
				negative_group_entries.remove( oldentry.getGroupId() );
			else
				positive_group_entries.remove( oldentry.getGroupId() );
			}

		//entries.put( entry.getAclEntryId(), entry );
		if ( entry.isNegative() )
			negative_group_entries.put( entry.getGroupId(), entry );
		else
			positive_group_entries.put( entry.getGroupId(), entry );
		return true;
		}

	/**
	 * The important method of the class - tests an access permission
	 * for a specified user.  Assuming default is to deny access.
	 *
	 * @param principal The user to check.
	 * @param permission The access right to check.
	 * @return If user has access right returns true.
	 */
	public boolean checkPermission( java.security.Principal principal, java.security.acl.Permission permission )
		{
		return checkPermission( principal, permission, false );
		}


	/**
	 * The important method of the class - tests an access permission
	 * for a specified user.
	 *
	 * @param principal The user to check.
	 * @param permission The access right to check.
	 * @param hasaccess The initial state true = allowed, false = denied so far.
	 * @return If user has access right returns true.
	 */
	public boolean checkPermission( java.security.Principal principal, java.security.acl.Permission permission, boolean hasaccess )
		{
		//  This method is not synchronized but should be thread safe because it
		//  uses a synchronized method call to enumerate the groups in the Hashtable.

		try
			{
			int i;
			Group[] groups;

			groups = getGroupArray( true, permission );
			for ( i=0; i<groups.length; i++ )
				{
				if ( groups[i]!=null && groups[i].isMember( principal ) )
					{
					hasaccess=true;
					break;
					}
				}

			groups = getGroupArray( false, permission );
			for ( i=0; i<groups.length; i++ )
				{
				if ( groups[i]!=null && groups[i].isMember( principal ) )
					{
					hasaccess=false;
					break;
					}
				}
			}
		catch ( Exception ex )
			{
			log.error(ex.getMessage(), ex );
			//fail safe - deny access if anything goes wrong.
			return false;
			}

		return hasaccess;
		}


	/**
	 * Returns an array of groups referenced in this ACL.
	 *
	 * @param positive Specifies whether to look for groups within positive or negative entries.
	 * @param permission Groups in entries with this access right are returned.
	 * @return Array of groups - some elements of array may be null.
	 */
	private synchronized Group[] getGroupArray( boolean positive, java.security.acl.Permission permission )
		{
		int i;
		Hashtable table = positive?positive_group_entries:negative_group_entries;
		org.bodington.server.realm.Group[] array
				= new org.bodington.server.realm.Group[table.size()];
		Enumeration enumeration;
		org.bodington.server.realm.AclEntry entry;

		enumeration=table.elements();
		for ( i=0; enumeration.hasMoreElements(); )
			{
			entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
			if ( !entry.checkPermission( permission ) )
				continue;
			array[i++] = (org.bodington.server.realm.Group)entry.getPrincipal();
			}

		while ( i< array.length )
			array[i++]=null;

		return array;
		}


	/**
	 * Get list of groups and individuals who have specified permission
	 *
	 * @param permission The access right to check.
	 * @return An enumeration of objects of type org.bodington.server.realm.Principal.
	 */
	public synchronized Enumeration everyoneWhoCan( java.security.acl.Permission permission )
		throws BuildingServerException
		{
		org.bodington.server.realm.Group entrygroup;

		org.bodington.server.realm.AclEntry entry;
		boolean hasaccess=false;

		Hashtable groups = new Hashtable();
		Vector principals = new Vector();

		Enumeration enumeration;
		for ( enumeration=positive_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
			if ( !entry.checkPermission( permission ) )
				continue;

			entrygroup = (org.bodington.server.realm.Group)entry.getPrincipal();
			groups.put( entrygroup, entrygroup );
			}
		// now have list of groups granted access

		for ( enumeration=negative_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
			if ( !entry.checkPermission( permission ) )
				continue;

			entrygroup = (org.bodington.server.realm.Group)entry.getPrincipal();
			if ( groups.containsKey( entrygroup ) )
				{
				groups.remove( entrygroup );
				}
			}
		// now have removed groups that have been denied access

		//amendment - if there are no groups don't bother looking for members!
		if ( groups.isEmpty() )
			return  principals.elements();

		StringBuffer where = new StringBuffer();
		where.append( "user_id IN (SELECT user_id FROM members WHERE group_id IN ( " );
		enumeration = groups.elements();
		for ( int i=0; enumeration.hasMoreElements(); i++ )
			{
			entrygroup = (org.bodington.server.realm.Group)enumeration.nextElement();
			principals.addElement( entrygroup);
			if ( i>0 )
				where.append( ", " );
			where.append( entrygroup.getGroupId().toString() );
			}
		where.append( " ) )" );

		enumeration = User.findUsers( where.toString(), "surname" );
		while ( enumeration.hasMoreElements() )
			principals.addElement( enumeration.nextElement() );

		return principals.elements();
		}

	/**
	 * Get table of groups who have specified permission
	 *
	 * @param permission The access right to check.
	 * @return An enumeration of objects of type org.bodington.server.realm.Principal.
	 */
	public synchronized Hashtable everyGroupWhoCan( java.security.acl.Permission permission )
		throws BuildingServerException
		{
		org.bodington.server.realm.Group entrygroup;
		Hashtable local_groups = new Hashtable();
		org.bodington.server.realm.AclEntry entry;
		boolean hasaccess=false;

		log.debug( "acl.everyGroupWhoCan( java.security.acl.Permission permission )" );

		Enumeration enumeration;
		for ( enumeration=positive_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
			if ( !entry.checkPermission( permission ) )
				continue;

			entrygroup = (org.bodington.server.realm.Group)entry.getPrincipal();
			local_groups.put( entrygroup.getPrimaryKey(), entrygroup );
			log.debug( "Found " + entrygroup.getName() );
			}
		// now have list of groups granted access

		for ( enumeration=negative_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
			if ( !entry.checkPermission( permission ) )
				continue;

			entrygroup = (org.bodington.server.realm.Group)entry.getPrincipal();
			if ( local_groups.containsKey( entrygroup.getPrimaryKey() ) )
				{
				local_groups.remove( entrygroup.getPrimaryKey() );
				}
			}
		// now have removed groups that have been denied access

		return local_groups;
		}

	/**
	 * Gets a list of entries.
	 *
	 * @return An Enumeration of all entries in the acl.
	 */
	public synchronized Enumeration entries()
		{
		Object entry;
		Vector fulllist=new Vector();
		Enumeration enumeration;
		for ( enumeration=positive_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = enumeration.nextElement();
			fulllist.addElement( entry );
			}
		for ( enumeration=negative_group_entries.elements(); enumeration.hasMoreElements(); )
			{
			entry = enumeration.nextElement();
			fulllist.addElement( entry );
			}

		return fulllist.elements();
		}

	/**
	 * Gets all the access rights of a specified user.
	 *
	 * @param user The user to test.
	 * @return An enumeration of permissions.
	 */
	public synchronized Enumeration getPermissions( java.security.Principal user )
		{
		Hashtable permissions=new Hashtable();

		org.bodington.server.realm.Group entrygroup;
		org.bodington.server.realm.AclEntry entry;
		org.bodington.server.realm.Permission permission;

		Enumeration enumeration, enum2;
		try
			{
			for ( enumeration=positive_group_entries.elements(); enumeration.hasMoreElements(); )
				{
				entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
				entrygroup = (org.bodington.server.realm.Group)entry.getPrincipal();
				if ( entrygroup.isMember( user ) )
					{
					// if principal is in this group go through all the permissions
					for ( enum2=entry.permissions(); enum2.hasMoreElements(); )
						{
						permission = (org.bodington.server.realm.Permission)enum2.nextElement();
						//only add new entries
						if ( !permissions.containsKey( permission ) )
							permissions.put( permission, permission );
						}
					}
				}

			for ( enumeration=negative_group_entries.elements(); enumeration.hasMoreElements(); )
				{
				entry = (org.bodington.server.realm.AclEntry)enumeration.nextElement();
				entrygroup = (org.bodington.server.realm.Group)entry.getPrincipal();
				if ( entrygroup.isMember( user ) )
					{
					// if principal is in this group go through all the permissions
					for ( enum2=entry.permissions(); enum2.hasMoreElements(); )
						{
						permission = (org.bodington.server.realm.Permission)enum2.nextElement();
						// if permission was previously granted remove it now
						if ( permissions.containsKey( permission ) )
							permissions.remove( permission );
						}
					}
				}
			}
		catch ( Exception ex )
			{
		    log.error(ex.getMessage(), ex );

			//fail safe - deny access if anything goes wrong.
			return (new Vector()).elements();
			}

		return permissions.elements();
		}

	/**
	 * Used to remove an entry.  Caller is determined from
	 * the threads BuildingContext.
	 *
	 * @param newentry The entry to remove.
	 * @return If successful returns true.
	 * @exception java.security.acl.NotOwnerException Thrown if the caller isn't an owner of the acl.
	 */
	public synchronized boolean removeEntry( java.security.acl.AclEntry newentry)
		throws NotOwnerException
		{
		BuildingContext context=BuildingContext.getContext();
		return removeEntry( context.getUser(), newentry );
		}

	/**
	 * Version of remove Entry required by interface.
	 *
	 * @param caller The user who is deleting the entry.
	 * @param newentry The entry to remove.
	 * @return If successful returns true.
	 * @exception java.security.acl.NotOwnerException Thrown if the caller isn't an owner of the acl.
	 */
	public synchronized boolean removeEntry( java.security.Principal caller, java.security.acl.AclEntry newentry)
		throws NotOwnerException
		{
		//if ( !isOwner( caller ) )
		//	throw new NotOwnerException();
		if ( !(newentry instanceof org.bodington.server.realm.AclEntry) )
			throw new NotOwnerException();

		org.bodington.server.realm.AclEntry entry = (org.bodington.server.realm.AclEntry)newentry;

		PrimaryKey group_id=entry.getGroupId();
		if ( entry.isNegative() )
			{
			if ( !negative_group_entries.containsKey( group_id ) )
				return false;
			negative_group_entries.remove( group_id );
			}
		else
			{
			if ( !positive_group_entries.containsKey( group_id ) )
				return false;
			positive_group_entries.remove( group_id );
			}
        entries_to_delete.addElement( newentry );
		return true;
		}

    /**
     * Gets an entry from the acl indexed by group_id and whether
     * it is a positive of negative index.
     *
     * @param group_id The id of the group requested.
     * @param negative Whether it is a negative entry.
     * @return The AclEntry.
     */
    public synchronized org.bodington.server.realm.AclEntry getAclEntry( PrimaryKey group_id, boolean negative )
        {
        if ( negative )
            return (AclEntry)negative_group_entries.get( group_id );
        else
            return (AclEntry)positive_group_entries.get( group_id );
        }


/**
 * Get the Group object representing the Resource owner group. <p>
 * <i>(WebLearn modification (method added): 02/12/2003 Colin Tatham)</i> <br />
 * <i>(WebLearn modification: modified to use owners_acl_entry variable: 26/07/2004 Colin Tatham)</i>
 * @return The owner group.
 * @exception BuildingServerException
 */
	public Group getOwnerGroup()
            throws BuildingServerException
        {
          if ( owners_acl_entry == null )
            loadReferencedObjects();

          if ( owners_acl_entry != null )
            return owners_acl_entry.getGroup();

          return null;
        }

/**
 * Get the AclEntry object which references the owner group. <p>
 * <i>(WebLearn modification (method added): 26/07/2004 Colin Tatham)</i>
 * @return The owner group AclEntry.
 * @exception BuildingServerException
 */
        public AclEntry getOwnerGroupAclEntry()
            throws BuildingServerException
        {
          if (owners_acl_entry == null)
            loadReferencedObjects();

          if (owners_acl_entry != null)
            return owners_acl_entry;

          return null;
        }


/**
 * Get the Group object representing the Resource ad hoc group. <p>
 * <i>(WebLearn modification (method added): 04/05/2004 Colin Tatham)</i>
 * @return The ad hoc group.
 * @exception BuildingServerException
 */
	public Group getAdHocGroup()
	    throws BuildingServerException
	{
	  if ( adhoc_group == null )
	    loadReferencedObjects();

	  return adhoc_group;
	}


	/**
	 * Set's the name of the acl and checks the caller.
	 *
	 * @param caller The user trying to change the name.
	 * @param name The new name.
	 * @exception java.security.acl.NotOwnerException Thrown if the caller isn't the owner.
	 */
	public void setName( java.security.Principal caller, String name)
		throws NotOwnerException
		{
		//if ( !isOwner( caller ) )
		//	throw new NotOwnerException();
		setName( name );
		}

	/**
	 * String representation of the ACl - just returns getName().
	 *
	 * @return The name of the acl.
	 */
	public String toString()
		{
		return getName();
		}
	
	/**
	 * Attempt to delete dependant AclEntry objects and the owners and
	 * adhoc group. We can't delete ourselves as we are still referenced.
	 * @throws BuildingServerException
	 */
	public void remove() throws BuildingServerException
	{
	    Enumeration aclEntries = entries();
	    while (aclEntries.hasMoreElements())
	    {
	        AclEntry aclEntry = (AclEntry)aclEntries.nextElement();
	        aclEntry.delete();
	    }
	    if (owners_group != null)
	    {
	        owners_group.remove();
	    }
	    if (adhoc_group != null)
	    {
	        adhoc_group.remove();
	    }
	}
    }
