/* * 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