package examples.security.rdbmsrealm;
import java.lang.reflect.InvocationTargetException;
import java.security.Principal;
import java.security.acl.Acl;
import java.security.acl.Group;
import java.security.acl.Permission;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import weblogic.logging.LogOutputStream;
import weblogic.security.acl.AbstractManageableRealm;
import weblogic.security.acl.AclImpl;
import weblogic.security.acl.BasicRealm;
import weblogic.security.acl.DebuggableRealm;
import weblogic.security.acl.User;
import weblogic.security.audit.Audit;
import weblogic.utils.UnsupportedOperationException;
import weblogic.utils.reuse.Pool;
/**
* RDBMS realm class. For historical reasons, the database schema we
* use does not allow empty groups or ACLs to be represented in the
* database. If you need to be able to represent empty groups or
* ACLs, you must rearrange the schema, which may require some code
* changes to this class and the RDBMSDelegate class. <p>
*
* This realm is mostly complete, but it does not implement some realm
* methods (notably ACL creation and deletion).
*
* @author Copyright (c) 1998-2000 by BEA Systems, Inc. All Rights Reserved.
*/
public class RDBMSRealm
extends AbstractManageableRealm
implements DebuggableRealm
{
/**
* The name of the realm properties file we will try to load.
*/
static final String RDBMS_PROPS = "rdbmsrealm.properties";
/**
* The name of the realm DDL file we will try to load.
*/
static final String RDBMS_DDL = "rdbmsrealm.ddl";
/**
* The maximum number of pooled database connections to maintain at
* any given time.
*/
private static final int DEFAULT_POOL_SIZE = 6;
/**
* We maintain a pool of delegates, each with its own connection to
* the database. Access to the pool itself is synchronized, but
* since only one thread will obtain a given delegate from the pool
* at any time, we don't need to synchronize any other method calls.
*
* @see #getDelegate
* @see #returnDelegate
* @see RDBMSDelegate$DFactory
*/
private Pool delegatePool;
/**
* The debugging log. This may be null.
*/
LogOutputStream log;
/**
* Create a new RDBMS realm object.
*/
public RDBMSRealm()
{
super("RDBMS Realm");
delegatePool = createPool(DEFAULT_POOL_SIZE);
}
/**
* Create a pool of delegates. The objects returned by the pool's
* factory method <i>must</i> extend the RDBMSDelegate class, or the
* server will not start.
*
* @param size the number of pool instances to maintain
*/
protected Pool createPool(int size)
{
return new Pool(new RDBMSDelegate.DFactory(this), size);
}
/**
* Obtain a delegate from the pool. If the pool is empty, this may
* cause a new delegate to be created and handed back to us. If a
* fatal error occurs when you are using a delegate, do not return
* it to the pool. You can do this by setting your reference to the
* delegate to null and calling returnDelegate() as usual; it will
* do the right thing.
*
* @see #returnDelegate
* @exception RDBMSException an error occurred in communicating with
* the database
*/
protected RDBMSDelegate getDelegate()
{
try
{
return (RDBMSDelegate) delegatePool.getInstance();
}
catch (InvocationTargetException e)
{
throw new RDBMSException("could not get delegate", e);
}
}
/**
* Return a delegate to the pool. If the delegate was set to null
* because of errors, nothing is done.
*
* @see #getDelegate
*/
protected void returnDelegate(RDBMSDelegate delegate)
{
if (delegate != null)
{
delegatePool.returnInstance(delegate);
}
}
/**
* Return the user with the given name, or null if the user does not
* exist in the database.
*
* @param name the name to obtain
* @return the user, or null if none
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public User getUser(String name)
{
// Most of the other methods in this class are patterned after
// this one. The delegate does all of the actual work. These
// methods simply obtain a delegate from the pool, call through to
// it, and handle any errors that arise.
// Obtain a delegate to talk to.
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getUser(name);
}
catch (SQLException e)
{
// If the delegate throws an exception, the connection to the
// database may be in an unsafe state. Make sure we don't use
// this delegate again.
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
// This will do nothing if the reference to the delegate has
// been set to null because of an error, so the delegate will
// not be returned to the pool.
returnDelegate(delegate);
}
}
/**
* Return the principal with the given name, or null if the
* principal does not exist in the database.
*
* @param name the name to obtain
* @return the principal, or null if none
* @exception RDBMSException an error occurred in communicating with
* the database
*/
protected Principal getPrincipal(String name)
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getPrincipal(name);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
protected Hashtable getGroupMembersInternal(String name)
{
// It's easiest to just call getGroup and use RDBMSGroup's
// getMembers method to return the membership of this group.
RDBMSGroup grp = (RDBMSGroup) getGroup(name);
if (grp == null)
{
return null;
} else {
return grp.getMembers();
}
}
/**
* Return the group with the given name, or null if the group does
* not exist in the database.
*
* @param name the name to obtain
* @return the group, or null if none
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Group getGroup(String name)
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getGroup(name);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Return the ACL with the given name, or null if the ACL does
* not exist in the database.
*
* @param name the name to obtain
* @return the ACL, or null if none
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Acl getAcl(String name)
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getAcl(name);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Return an enumeration of all users in the database. Each element
* of the Enumeration is a User object.
*
* @return all users
* @see weblogic.security.acl.User
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Enumeration getUsers()
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getUsers();
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Add a member to a group.
*
* @param group the group to add to
* @param principal the principal to add
* @return true
* @exception RDBMSException an error occurred in communicating with
* the database
*/
boolean addGroupMember(RDBMSGroup group, Principal member)
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.addGroupMember(group, member);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Remove a member from a group.
*
* @param group the group to remove from
* @param principal the principal to remove
* @return true
* @exception RDBMSException an error occurred in communicating with
* the database
*/
boolean removeGroupMember(RDBMSGroup group, Principal member)
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.removeGroupMember(group, member);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Return an enumeration of all groups in the database. Each
* element of the Enumeration is a Group object. Note that in this
* realm, empty groups cannot currently exist.
*
* @return all groups
* @see java.security.acl.Group
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Enumeration getGroups()
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getGroups();
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Return an enumeration of all ACLs in the database. Each element
* of the Enumeration is an Acl object. Note that in this realm,
* empty ACLs cannot currently exist.
*
* @return all ACLs
* @see java.security.acl.Acl
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Enumeration getAcls()
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getAcls();
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Return an enumeration of all permissions in the database. Each
* element of the Enumeration is a Permission object.
*
* @return all permissions
* @see java.security.acl.Permission
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Enumeration getPermissions()
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getPermissions();
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Authenticate the given user. If the authentication step passes,
* a User object is returned for that user, otherwise null is
* returned.
*
* @return the authenticated user, or null
* @exception RDBMSException an error occurred in communicating with
* the database
*/
protected User authUserPassword(String name, String passwd)
{
RDBMSUser user = (RDBMSUser) getUser(name);
if (user != null && user.authenticate(passwd) == false)
{
user = null;
}
return user;
}
/**
* Get a Permission that matches the specified name. If no such
* permission exists in the database, null is returned.
*
* @param name the name of the permission
* @return permission object, or nullp
* @exception RDBMSException an error occurred in communicating with
* the database
*/
public Permission getPermission(String name)
{
RDBMSDelegate delegate = getDelegate();
try
{
return delegate.getPermission(name);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Create a new User.
*
* @param name the name of the new user
* @param credential the credential for the user (must be a plaintext password)
* @param constraints null, for this realm
* @return the new User
* @exception SecurityException invalid credential or constraint
*/
public User newUser(String name, Object credential, Object constraints)
throws SecurityException
{
RDBMSDelegate delegate = getDelegate();
if (constraints != null)
{
throw new SecurityException("newUser does not support constraints");
}
if (credential == null || !(credential instanceof String))
{
throw new SecurityException("invalid credential");
}
try
{
return delegate.newUser(name, (String) credential);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Delete a user. This removes the user from the users table, and
* also from the group membership and ACL entry tables. <p>
*
* <b>Warning</b>: due to the schema layout, if you delete a user
* that is the only member of one or more groups or ACLs, those
* groups or ACLs will also disappear! This may cause unexpected
* exceptions at runtime.
*
* @param user the user to delete
* @exception SecurityException invalid user
*/
public void deleteUser(User user)
throws SecurityException
{
RDBMSDelegate delegate = getDelegate();
if (!(user instanceof RDBMSUser))
{
throw new SecurityException("invalid user type: " + user.getClass().getName());
}
try
{
delegate.deleteUser(user);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Delete a group. This deletes the group from both the group
* membership table and the ACL entry table. <p>
*
* <b>Warning</b>: due to the schema layout, if you delete a group
* that is the only member of one or more ACLs, those ACLs will also
* disappear! This may cause unexpected exceptions at runtime.
*
* @param group the group to delete
* @exception SecurityException invalid group
*/
public void deleteGroup(Group group)
throws SecurityException
{
RDBMSDelegate delegate = getDelegate();
if (!(group instanceof RDBMSGroup))
{
throw new SecurityException("invalid group type: " +
group.getClass().getName());
}
try
{
delegate.deleteGroup(group);
}
catch (SQLException e)
{
delegate.close();
delegate = null;
throw new RDBMSException("caught SQL exception", e);
}
finally
{
returnDelegate(delegate);
}
}
/**
* Enable or disable debug logging.
*/
public void setDebug(boolean enable)
{
if (enable && log == null)
{
log = new LogOutputStream("RDBMSRealm");
}
if (!enable)
{
log = null;
}
}
/**
* Obtain the debug log, if it is enabled.
*/
public LogOutputStream getDebugLog()
{
return log;
}
/**
* Factory method for creating new user objects.
*/
User createUser(String name, String passwd)
{
return new RDBMSUser(name, passwd, this);
}
/**
* Factory method for creating new group objects.
*/
RDBMSGroup createGroup(String name, Hashtable members)
{
return new RDBMSGroup(name, this, members);
}
}