/*
* 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 .
*
* 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 ,
* 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; iObjectProvider.
*
* @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