628 lines
20 KiB
Java
628 lines
20 KiB
Java
/*
|
|
* The contents of this file are subject to the Mozilla Public License Version 1.1
|
|
* (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.mozilla.org/MPL/>.
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
|
|
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
|
|
* language governing rights and limitations under the License.
|
|
*
|
|
* The Original Code is the Venice Web Communities System.
|
|
*
|
|
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
|
|
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
|
|
* Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
package com.silverwrist.dynamo.db;
|
|
|
|
import java.security.Principal;
|
|
import java.security.acl.AclNotFoundException;
|
|
import java.util.*;
|
|
import org.apache.commons.collections.*;
|
|
import com.silverwrist.dynamo.Namespaces;
|
|
import com.silverwrist.dynamo.UserInfoNamespace;
|
|
import com.silverwrist.dynamo.except.*;
|
|
import com.silverwrist.dynamo.iface.*;
|
|
import com.silverwrist.dynamo.security.SecurityReferenceMonitor;
|
|
import com.silverwrist.dynamo.util.*;
|
|
|
|
class GroupObject implements DynamoGroup
|
|
{
|
|
/*--------------------------------------------------------------------------------
|
|
* Static data members
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static final String PERM_NAMESPACE = Namespaces.GROUP_PERMISSIONS_NAMESPACE;
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Attributes
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private GroupObjectOps m_ops; // operations object
|
|
private NamespaceCache m_ns_cache; // namespace cache object
|
|
private SecurityReferenceMonitor m_srm; // security reference monitor
|
|
private UserProxyManagement m_upm; // user proxy manager
|
|
private int m_gid; // group ID
|
|
private String m_groupname; // group name
|
|
private int m_gaclid; // group ACL ID
|
|
private ReferenceMap m_properties; // cached property values
|
|
private Object m_properties_sync = new Object(); // synchronization for above
|
|
private HashSet m_known_members = new HashSet(); // known members
|
|
private HashSet m_known_nonmembers = new HashSet(); // known nonmembers
|
|
private Object m_members_sync = new Object(); // synchronization for above
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Constructor
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
GroupObject(Map data, GroupObjectOps ops, NamespaceCache ns_cache, SecurityReferenceMonitor srm,
|
|
UserProxyManagement upm)
|
|
{
|
|
m_ops = ops;
|
|
m_ns_cache = ns_cache;
|
|
m_srm = srm;
|
|
m_upm = upm;
|
|
m_gid = ((Integer)(data.get(UserManagerOps.HMKEY_GID))).intValue();
|
|
m_groupname = (String)(data.get(UserManagerOps.HMKEY_GROUPNAME));
|
|
m_gaclid = ((Integer)(data.get(UserManagerOps.HMKEY_GACLID))).intValue();
|
|
m_properties = new ReferenceMap(ReferenceMap.HARD,ReferenceMap.SOFT);
|
|
|
|
} // end constructor
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Internal operations
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
private static final int getPrincipalUID(Principal p)
|
|
{
|
|
if (p instanceof DynamoUser)
|
|
return ((DynamoUser)p).getUID();
|
|
else
|
|
return -1;
|
|
|
|
} // end getPrincipalUID
|
|
|
|
private final void testOperationWithoutSecurity()
|
|
{
|
|
if (m_gaclid==-1)
|
|
return;
|
|
throw new GroupRuntimeException(new DynamoSecurityException(GroupObject.class,"DatabaseMessages",
|
|
"sec.needSec"));
|
|
|
|
} // end testOperationWithoutSecurity
|
|
|
|
private final void testPermission(DynamoUser caller, String perm_namespace, String perm_name,
|
|
String fail_message) throws DatabaseException, DynamoSecurityException
|
|
{
|
|
if (m_gaclid==-1)
|
|
return;
|
|
if (caller==null)
|
|
throw new DynamoSecurityException(GroupObject.class,"DatabaseMessages","sec.needSec");
|
|
if (caller.equals(m_srm.getAdminUser()))
|
|
return; // Administrator can do anything
|
|
if (m_srm.testPermission(m_gaclid,caller,perm_namespace,perm_name))
|
|
return; // we have the right permission in the ACL
|
|
DynamoSecurityException dse = new DynamoSecurityException(GroupObject.class,"DatabaseMessages",
|
|
fail_message);
|
|
dse.setParameter(0,m_groupname);
|
|
throw dse;
|
|
|
|
} // end testPermission
|
|
|
|
private final void testPermission(DynamoUser caller, int target_uid, String perm_namespace, String perm_name,
|
|
String perm2_name, String fail_message)
|
|
throws DatabaseException, DynamoSecurityException
|
|
{
|
|
if (m_gaclid==-1)
|
|
return;
|
|
if (caller==null)
|
|
throw new DynamoSecurityException(GroupObject.class,"DatabaseMessages","sec.needSec");
|
|
if (caller.equals(m_srm.getAdminUser()))
|
|
return; // Administrator can do anything
|
|
if ((caller.getUID()==target_uid) && m_srm.testPermission(m_gaclid,caller,perm_namespace,perm2_name))
|
|
return; // we can "join group" or "unjoin group" if we have the right permissions
|
|
if (m_srm.testPermission(m_gaclid,caller,perm_namespace,perm_name))
|
|
return; // we have the right permission in the ACL
|
|
DynamoSecurityException dse = new DynamoSecurityException(GroupObject.class,"DatabaseMessages",
|
|
fail_message);
|
|
dse.setParameter(0,m_groupname);
|
|
throw dse;
|
|
|
|
} // end testPermission
|
|
|
|
private final void testAclOwner(DynamoUser caller) throws DatabaseException, DynamoSecurityException
|
|
{
|
|
if (m_gaclid==-1)
|
|
return;
|
|
if (caller==null)
|
|
throw new DynamoSecurityException(GroupObject.class,"DatabaseMessages","sec.needSec");
|
|
if (caller.equals(m_srm.getAdminUser()))
|
|
return; // Administrator can do anything
|
|
if (m_srm.isOwnerOfAcl(m_gaclid,caller))
|
|
return; // we own this ACL
|
|
DynamoSecurityException dse = new DynamoSecurityException(GroupObject.class,"DatabaseMessages",
|
|
"sec.setGroupAcl");
|
|
dse.setParameter(0,m_groupname);
|
|
throw dse;
|
|
|
|
} // end testAclOwner
|
|
|
|
private final boolean addUIDInternal(int uid) throws DatabaseException
|
|
{
|
|
Integer key = new Integer(uid);
|
|
synchronized (m_members_sync)
|
|
{ // look in the cache first
|
|
if (m_known_members.contains(key))
|
|
return false; // we're already a known member
|
|
|
|
// fiddle the database
|
|
boolean rc = m_ops.addMember(m_gid,uid);
|
|
m_known_members.add(key); // update the cache to reflect reality
|
|
if (m_known_nonmembers!=null)
|
|
m_known_nonmembers.remove(key);
|
|
return rc;
|
|
|
|
} // end synchronized block
|
|
|
|
} // end addUIDInternal
|
|
|
|
private final boolean removeUIDInternal(int uid) throws DatabaseException
|
|
{
|
|
Integer key = new Integer(uid);
|
|
synchronized (m_members_sync)
|
|
{ // look in the cache first
|
|
if (m_known_nonmembers!=null)
|
|
{ // are we a known nonmember?
|
|
if (m_known_nonmembers.contains(key))
|
|
return false; // already a nonmember
|
|
|
|
} // end if
|
|
else
|
|
{ // in this circumstance, everyone not in the members set is a nonmember
|
|
if (!(m_known_members.contains(key)))
|
|
return false; // already a nonmember
|
|
|
|
} // end else
|
|
|
|
// fiddle the database
|
|
boolean rc = m_ops.removeMember(m_gid,uid);
|
|
m_known_members.remove(key); // update the cache to reflect reality
|
|
if (m_known_nonmembers!=null)
|
|
m_known_nonmembers.add(key);
|
|
return rc;
|
|
|
|
} // end synchronized block
|
|
|
|
} // end removeUIDInternal
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface Principal
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public boolean equals(Object another)
|
|
{
|
|
if (another==null)
|
|
return false;
|
|
if (another instanceof DynamoGroup)
|
|
return (m_gid==((DynamoGroup)another).getGID());
|
|
return false;
|
|
|
|
} // end equals
|
|
|
|
public String toString()
|
|
{
|
|
return "group " + m_groupname;
|
|
|
|
} // end toString
|
|
|
|
public int hashCode()
|
|
{
|
|
return m_gid;
|
|
|
|
} // end hashCode
|
|
|
|
public String getName()
|
|
{
|
|
return m_groupname;
|
|
|
|
} // end getName
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface Group
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public boolean addMember(Principal user)
|
|
{
|
|
testOperationWithoutSecurity();
|
|
try
|
|
{ // break down the operations...
|
|
if (user instanceof DynamoUser)
|
|
return this.addUIDInternal(((DynamoUser)user).getUID());
|
|
else if (user instanceof DynamoGroup)
|
|
{ // add all UIDs contained in this group
|
|
int[] uids = ((DynamoGroup)user).getUIDs();
|
|
boolean rc = false;
|
|
for (int i=0; i<uids.length; i++)
|
|
rc = this.addUIDInternal(uids[i]) || rc;
|
|
return rc;
|
|
|
|
} // end else if
|
|
else
|
|
throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");
|
|
|
|
} // end try
|
|
catch (DatabaseException e)
|
|
{ // wrap this exception and return it
|
|
throw new GroupRuntimeException(e);
|
|
|
|
} // end catch
|
|
|
|
} // end addMember
|
|
|
|
public boolean removeMember(Principal user)
|
|
{
|
|
testOperationWithoutSecurity();
|
|
try
|
|
{ // break down the operations...
|
|
if (user instanceof DynamoUser)
|
|
return this.removeUIDInternal(((DynamoUser)user).getUID());
|
|
else if (user instanceof DynamoGroup)
|
|
{ // remove all UIDs contained in this group
|
|
int[] uids = ((DynamoGroup)user).getUIDs();
|
|
boolean rc = false;
|
|
for (int i=0; i<uids.length; i++)
|
|
rc = this.removeUIDInternal(uids[i]) || rc;
|
|
return rc;
|
|
|
|
} // end else if
|
|
else
|
|
throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");
|
|
|
|
} // end try
|
|
catch (DatabaseException e)
|
|
{ // wrap this exception and return it
|
|
throw new GroupRuntimeException(e);
|
|
|
|
} // end catch
|
|
|
|
} // end removeMember
|
|
|
|
public boolean isMember(Principal member)
|
|
{
|
|
try
|
|
{ // break down the operations...
|
|
if (member instanceof DynamoUser)
|
|
return this.testUID(((DynamoUser)member).getUID());
|
|
else if (member instanceof DynamoGroup)
|
|
{ // test all UIDs contained within this group
|
|
int[] uids = ((DynamoGroup)member).getUIDs();
|
|
if (uids.length==0)
|
|
return false;
|
|
boolean rc = true;
|
|
for (int i=0; rc && (i<uids.length); i++)
|
|
rc = this.testUID(uids[i]);
|
|
return rc;
|
|
|
|
} // end else if
|
|
else
|
|
throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");
|
|
|
|
} // end try
|
|
catch (DatabaseException e)
|
|
{ // wrap this exception and return it
|
|
throw new GroupRuntimeException(e);
|
|
|
|
} // end catch
|
|
|
|
} // end isMember
|
|
|
|
public Enumeration members()
|
|
{
|
|
try
|
|
{ // call down, get the UIDs, and use them to get proxies
|
|
int[] uids = this.getUIDs();
|
|
ArrayList tmp = new ArrayList(uids.length);
|
|
for (int i=0; i<uids.length; i++)
|
|
tmp.add(m_upm.getUserProxy(uids[i]));
|
|
return Collections.enumeration(tmp);
|
|
|
|
} // end try
|
|
catch (DatabaseException e)
|
|
{ // wrap this exception and return it
|
|
throw new GroupRuntimeException(e);
|
|
|
|
} // end catch
|
|
|
|
} // end members
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface ObjectProvider
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
/**
|
|
* Retrieves an object from this <CODE>ObjectProvider</CODE>.
|
|
*
|
|
* @param namespace The namespace to interpret the name relative to.
|
|
* @param name The name of the object to be retrieved.
|
|
* @return The object reference specified.
|
|
*/
|
|
public Object getObject(String namespace, String name)
|
|
{
|
|
try
|
|
{ // convert the namespace name to an ID here
|
|
PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace),name);
|
|
Object rc = null;
|
|
synchronized (m_properties_sync)
|
|
{ // start by looking in the properties map
|
|
rc = m_properties.get(key);
|
|
if (rc==null)
|
|
{ // no use - need to try the database
|
|
rc = m_ops.getProperty(m_gid,key);
|
|
if (rc!=null)
|
|
m_properties.put(key,rc);
|
|
|
|
} // end if
|
|
|
|
} // end synchronized block
|
|
|
|
if (rc==null)
|
|
throw new NoSuchObjectException(this.toString(),namespace,name);
|
|
return rc;
|
|
|
|
} // end try
|
|
catch (DatabaseException e)
|
|
{ // translate into our NoSuchObjectException but retain the DatabaseException
|
|
throw new NoSuchObjectException(this.toString(),namespace,name,e);
|
|
|
|
} // end catch
|
|
|
|
} // end getObject
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface SecureObjectStore
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public Object setObject(DynamoUser caller, String namespace, String name, Object value)
|
|
throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testPermission(caller,namespace,"set.property","sec.setGroupProperty");
|
|
Object rc = null;
|
|
// convert the namespace name to an ID here
|
|
PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace),name);
|
|
synchronized (m_properties_sync)
|
|
{ // start by setting the database value
|
|
rc = m_ops.setProperty(m_gid,key,value);
|
|
|
|
// and cache it, too
|
|
m_properties.put(key,value);
|
|
|
|
} // end synchronized block
|
|
|
|
return rc;
|
|
|
|
} // end setObject
|
|
|
|
public Object removeObject(DynamoUser caller, String namespace, String name)
|
|
throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testPermission(caller,namespace,"remove.property","sec.removeGroupProperty");
|
|
Object rc = null;
|
|
// convert the namespace name to an ID here
|
|
PropertyKey key = new PropertyKey(m_ns_cache.namespaceNameToId(namespace),name);
|
|
synchronized (m_properties_sync)
|
|
{ // start by killing the database value
|
|
rc = m_ops.removeProperty(m_gid,key);
|
|
|
|
// and remove the cached value, too
|
|
m_properties.remove(key);
|
|
|
|
} // end synchronized block
|
|
|
|
return rc;
|
|
|
|
} // end removeObject
|
|
|
|
public Collection getNamespaces() throws DatabaseException
|
|
{
|
|
// call through to the database to get the list of namespace IDs
|
|
int[] ids = m_ops.getPropertyNamespaceIDs(m_gid);
|
|
|
|
ArrayList rc = new ArrayList(ids.length);
|
|
for (int i=0; i<ids.length; i++)
|
|
rc.add(m_ns_cache.namespaceIdToName(ids[i]));
|
|
return Collections.unmodifiableList(rc);
|
|
|
|
} // end getNamespaces
|
|
|
|
public Collection getNamesForNamespace(String namespace) throws DatabaseException
|
|
{
|
|
// call through to the database to get the data for this namespace
|
|
int nsid = m_ns_cache.namespaceNameToId(namespace);
|
|
Map data = m_ops.getAllProperties(m_gid,nsid);
|
|
|
|
// we both create the return value and cache the data values
|
|
ArrayList rc = new ArrayList(data.size());
|
|
synchronized (m_properties_sync)
|
|
{ // do the transfer...
|
|
Iterator it = data.entrySet().iterator();
|
|
while (it.hasNext())
|
|
{ // copy one entry at a time
|
|
Map.Entry ntry = (Map.Entry)(it.next());
|
|
rc.add(ntry.getKey().toString());
|
|
m_properties.put(new PropertyKey(nsid,ntry.getKey().toString()),ntry.getValue());
|
|
|
|
} // end while
|
|
|
|
} // end synchronized block
|
|
|
|
return Collections.unmodifiableList(rc);
|
|
|
|
} // end getNamesForNamespace
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
* Implementations from interface DynamoGroup
|
|
*--------------------------------------------------------------------------------
|
|
*/
|
|
|
|
public int getGID()
|
|
{
|
|
return m_gid;
|
|
|
|
} // end getGID
|
|
|
|
public boolean addUID(DynamoUser caller, int uid) throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testPermission(caller,uid,PERM_NAMESPACE,"add.member","join.group","sec.addGroupMember");
|
|
return addUIDInternal(uid);
|
|
|
|
} // end addUID
|
|
|
|
public boolean removeUID(DynamoUser caller, int uid) throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testPermission(caller,uid,PERM_NAMESPACE,"remove.member","unjoin.group","sec.removeGroupMember");
|
|
return removeUIDInternal(uid);
|
|
|
|
} // end removeUID
|
|
|
|
public boolean testUID(int uid) throws DatabaseException
|
|
{
|
|
Integer key = new Integer(uid);
|
|
synchronized (m_members_sync)
|
|
{ // look in the cache first
|
|
if (m_known_members.contains(key))
|
|
return true;
|
|
if ((m_known_nonmembers==null) || m_known_nonmembers.contains(key))
|
|
return false;
|
|
|
|
// try the database
|
|
boolean rc = m_ops.testMember(m_gid,uid);
|
|
if (rc) // add cached information
|
|
m_known_members.add(key);
|
|
else if (m_known_nonmembers!=null)
|
|
m_known_nonmembers.add(key);
|
|
return rc;
|
|
|
|
} // end synchronized block
|
|
|
|
} // end testUID
|
|
|
|
public int[] getUIDs() throws DatabaseException
|
|
{
|
|
int[] rc = null;
|
|
synchronized (m_members_sync)
|
|
{ // try the cache first
|
|
if (m_known_nonmembers==null)
|
|
{ // we know we already contain the complete members list, so just get that
|
|
rc = new int[m_known_members.size()];
|
|
int i = 0;
|
|
Iterator it = m_known_members.iterator();
|
|
while (it.hasNext())
|
|
rc[i++] = ((Integer)(it.next())).intValue();
|
|
|
|
} // end if
|
|
else
|
|
{ // have to call down to the database to get the list
|
|
rc = m_ops.getMembers(m_gid);
|
|
|
|
// now we can cache the entire array and dump the nonmembers cache
|
|
for (int i=0; i<rc.length; i++)
|
|
m_known_members.add(new Integer(rc[i]));
|
|
m_known_nonmembers.clear();
|
|
m_known_nonmembers = null;
|
|
|
|
} // end else
|
|
|
|
} // end synchronized block
|
|
|
|
return rc;
|
|
|
|
} // end getUIDs
|
|
|
|
public boolean addMember(DynamoUser caller, Principal member)
|
|
throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testPermission(caller,getPrincipalUID(member),PERM_NAMESPACE,"add.member","join.group",
|
|
"sec.addGroupMember");
|
|
if (member instanceof DynamoUser)
|
|
return this.addUIDInternal(((DynamoUser)member).getUID());
|
|
else if (member instanceof DynamoGroup)
|
|
{ // add all UIDs contained in this group
|
|
int[] uids = ((DynamoGroup)member).getUIDs();
|
|
boolean rc = false;
|
|
for (int i=0; i<uids.length; i++)
|
|
rc = this.addUIDInternal(uids[i]) || rc;
|
|
return rc;
|
|
|
|
} // end else if
|
|
else
|
|
throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");
|
|
|
|
} // end addMember
|
|
|
|
public boolean removeMember(DynamoUser caller, Principal member)
|
|
throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testPermission(caller,getPrincipalUID(member),PERM_NAMESPACE,"remove.member","unjoin.group",
|
|
"sec.removeGroupMember");
|
|
if (member instanceof DynamoUser)
|
|
return this.removeUIDInternal(((DynamoUser)member).getUID());
|
|
else if (member instanceof DynamoGroup)
|
|
{ // remove all UIDs contained in this group
|
|
int[] uids = ((DynamoGroup)member).getUIDs();
|
|
boolean rc = false;
|
|
for (int i=0; i<uids.length; i++)
|
|
rc = this.removeUIDInternal(uids[i]) || rc;
|
|
return rc;
|
|
|
|
} // end else if
|
|
else
|
|
throw new IllegalArgumentException("expected DynamoUser or DynamoGroup");
|
|
|
|
} // end removeMember
|
|
|
|
public DynamoAcl getAcl() throws DatabaseException, AclNotFoundException
|
|
{
|
|
if (m_gaclid==-1)
|
|
return null;
|
|
else
|
|
return m_srm.getAcl(m_gaclid);
|
|
|
|
} // end getAcl
|
|
|
|
public void setAcl(DynamoUser caller, DynamoAcl acl) throws DatabaseException, DynamoSecurityException
|
|
{
|
|
testAclOwner(caller);
|
|
int new_id = ((acl==null) ? -1 : acl.getAclID());
|
|
m_ops.setAclID(m_gid,new_id);
|
|
m_gaclid = new_id;
|
|
|
|
} // end setAcl
|
|
|
|
public synchronized int getMemberCount() throws DatabaseException
|
|
{
|
|
return m_ops.getMemberCount(m_gid);
|
|
|
|
} // end getMemberCount
|
|
|
|
public List getMembers(int offset, int count) throws DatabaseException
|
|
{
|
|
int[] ids = m_ops.getMembers(m_gid,offset,count);
|
|
ArrayList rc = new ArrayList(ids.length);
|
|
for (int i=0; i<ids.length; i++)
|
|
rc.add(m_upm.getUserProxy(ids[i]));
|
|
return Collections.unmodifiableList(rc);
|
|
|
|
} // end getMembers
|
|
|
|
} // end class GroupObject
|