diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java
index eadbd82675889f7a1372a238df23935ddf4690ad..9157966d3b1ca264d9575daf7ad5aca5a5889adf 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java
@@ -86,9 +86,11 @@ public class LdapConfig
     public static final String LDAP_PASSWD = "passwd";
     public static final String LDAP_USERS_DN = "usersDn";
     public static final String LDAP_GROUPS_DN = "groupsDn";
+    public static final String LDAP_ADMIN_GROUPS_DN  = "adminGroupsDn";
 
     private String usersDN;
     private String groupsDN;
+    private String adminGroupsDN;
     private String server;
     private int port;
     private String adminUserDN;
@@ -156,18 +158,26 @@ public class LdapConfig
             throw new RuntimeException("failed to read property " + 
                                        LDAP_GROUPS_DN);
         }
+        
+        String ldapAdminGroupsDn = config.getProperty(LDAP_ADMIN_GROUPS_DN);
+        if (!StringUtil.hasText(ldapAdminGroupsDn))
+        {
+            throw new RuntimeException("failed to read property " + 
+                                       LDAP_ADMIN_GROUPS_DN);
+        }
 
         return new LdapConfig(server, Integer.valueOf(port), ldapAdmin, 
-                              ldapPasswd, ldapUsersDn, ldapGroupsDn);
+                              ldapPasswd, ldapUsersDn, ldapGroupsDn,
+                              ldapAdminGroupsDn);
     }
 
     public LdapConfig(String server, int port, String adminUserDN, 
-                      String adminPasswd, String usersDN, String groupsDN)
+                      String adminPasswd, String usersDN, String groupsDN,
+                      String adminGroupsDN)
     {
         if (!StringUtil.hasText(server))
         {
-            throw new IllegalArgumentException("Illegal LDAP server name: " + 
-                                               server);
+            throw new IllegalArgumentException("Illegal LDAP server name");
         }
         if (port < 0)
         {
@@ -176,23 +186,23 @@ public class LdapConfig
         }
         if (!StringUtil.hasText(adminUserDN))
         {
-            throw new IllegalArgumentException("Illegal Admin DN: " + 
-                                               adminUserDN);
+            throw new IllegalArgumentException("Illegal Admin DN");
         }
         if (!StringUtil.hasText(adminPasswd))
         {
-            throw new IllegalArgumentException("Illegal Admin password: " + 
-                                               adminPasswd);
+            throw new IllegalArgumentException("Illegal Admin password");
         }
         if (!StringUtil.hasText(usersDN))
         {
-            throw new IllegalArgumentException("Illegal users LDAP DN: " + 
-                                               usersDN);
+            throw new IllegalArgumentException("Illegal users LDAP DN");
         }
         if (!StringUtil.hasText(groupsDN))
         {
-            throw new IllegalArgumentException("Illegal groups LDAP DN: " + 
-                                               groupsDN);
+            throw new IllegalArgumentException("Illegal groups LDAP DN");
+        }
+        if (!StringUtil.hasText(adminGroupsDN))
+        {
+            throw new IllegalArgumentException("Illegal admin groups LDAP DN");
         }
 
         this.server = server;
@@ -201,6 +211,7 @@ public class LdapConfig
         this.adminPasswd = adminPasswd;
         this.usersDN = usersDN;
         this.groupsDN = groupsDN;
+        this.adminGroupsDN = adminGroupsDN;
     }
 
     public String getUsersDN()
@@ -212,6 +223,11 @@ public class LdapConfig
     {
         return this.groupsDN;
     }
+    
+    public String getAdminGroupsDN()
+    {
+        return this.adminGroupsDN;
+    }
 
     public String getServer()
     {
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
index edd396bb76c551e1d976aba97130abe2bb7d17de..13580e05434cace85e7e44d8ecc8aad18d5ec9ca 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
@@ -72,8 +72,8 @@ import java.security.AccessControlException;
 import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Date;
 import java.util.List;
+import java.util.Set;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -102,20 +102,11 @@ import com.unboundid.ldap.sdk.SearchResult;
 import com.unboundid.ldap.sdk.SearchResultEntry;
 import com.unboundid.ldap.sdk.SearchScope;
 import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl;
-import java.util.logging.Level;
 
 public class LdapGroupDAO<T extends Principal> extends LdapDAO
 {
     private static final Logger logger = Logger.getLogger(LdapGroupDAO.class);
-    
-    private static final String ACTUAL_GROUP_TOKEN = "<ACTUAL_GROUP>";
-    private static final String GROUP_READ_ACI = "(targetattr = \"*\") " + 
-            "(version 3.0;acl \"Group Read\";allow (read,compare,search)" + 
-            "(groupdn = \"ldap:///<ACTUAL_GROUP>\");)";
-    private static final String GROUP_WRITE_ACI = "(targetattr = \"*\") " + 
-            "(version 3.0;acl \"Group Write\";allow " + 
-            "(read,compare,search,selfwrite,write,add)" + 
-            "(groupdn = \"ldap:///<ACTUAL_GROUP>\");)";
+
     
     private LdapUserDAO<T> userPersist;
 
@@ -151,22 +142,11 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             throw new IllegalArgumentException("Group owner must be specified");
         }
         
-        try
-        {
-            User<X500Principal> subjectUser = 
-                    userPersist.getMember(getSubjectDN());
-            if (!subjectUser.equals(group.getOwner()))
-            {
-                throw new AccessControlException("Group owner must be group " + 
-                                                 " creator");
-            }
-        }
-        catch (LDAPException e)
+        if (!isCreatorOwner(group.getOwner()))
         {
-            e.printStackTrace();
-            throw new RuntimeException(e);
+            throw new AccessControlException("Group owner must be creator");
         }
-        
+
         try
         {
             getGroup(group.getID());
@@ -175,127 +155,39 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         catch (GroupNotFoundException ex)
         {
             try
-            {             
-                try
-                {
-                    Group inactiveGroup = getInactiveGroup(group.getID());
-
-                    // Check requestor owns the group.
-                    DN ownerDN = userPersist.getUserDN(group.getOwner());
-                    if (!ownerDN.equals(getSubjectDN()))
-                    {
-                       throw new AccessControlException(
-                           "Unable to activate group " + group.getID() + 
-                           " because " + getSubjectDN().toString()
-                           + " is not the owner"); 
-                    }
-                    
-                    List<Modification> mods = new ArrayList<Modification>();
-                    Modification mod = 
-                        new Modification(ModificationType.DELETE, 
-                                         "nsaccountlock");
-                    mods.add(mod);
-                    
-                    Group modifiedGroup = modifyGroup(group, inactiveGroup, 
-                                                      mods);
-                    Group activatedGroup = 
-                            new ActivatedGroup(modifiedGroup.getID(),
-                                               modifiedGroup.getOwner());
-                    activatedGroup.description = modifiedGroup.description;
-                    activatedGroup.groupRead = modifiedGroup.groupRead;
-                    activatedGroup.groupWrite = modifiedGroup.groupWrite;
-                    activatedGroup.getProperties()
-                            .addAll(modifiedGroup.getProperties());
-                    activatedGroup.getGroupMembers()
-                            .addAll(modifiedGroup.getGroupMembers());
-                    activatedGroup.getUserMembers()
-                            .addAll(modifiedGroup.getUserMembers());
-                    return activatedGroup;
-                }
-                catch (GroupNotFoundException ignore) {}
-                
+            {        
                 if (!group.getProperties().isEmpty())
                 {
                     throw new UnsupportedOperationException(
                             "Support for groups properties not available");
                 }
-
-                DN ownerDN = userPersist.getUserDN(group.getOwner());
-                String groupWriteAci = null;
-                String groupReadAci = null;
-                if (group.groupWrite != null)
-                {
-                    DN groupWrite = getGroupDN(group.groupWrite.getID());
-                    groupWriteAci = GROUP_WRITE_ACI.replace(
-                            ACTUAL_GROUP_TOKEN, 
-                            groupWrite.toNormalizedString());
-                }
-
-                if (group.groupRead != null)
-                {
-                    DN groupRead = getGroupDN(group.groupRead.getID());
-                    groupReadAci = GROUP_READ_ACI.replace(
-                            ACTUAL_GROUP_TOKEN, 
-                            groupRead.toNormalizedString());
-                }
-
-                // add new group
-                List<Attribute> attributes = new ArrayList<Attribute>();
-                attributes.add(new Attribute("objectClass", 
-                                             "groupofuniquenames"));
-
-                attributes.add(new Attribute("cn", group.getID()));
-                if (group.description != null)
-                {
-                    attributes.add(new Attribute("description", 
-                                                 group.description));
-                }
-
-                attributes.add(new Attribute("owner", 
-                                             ownerDN.toNormalizedString()));
-
-                // acis
-                List<String> acis = new ArrayList<String>();
-                if (groupWriteAci != null)
-                {
-                    acis.add(groupWriteAci);
-                }
-                if (groupReadAci != null)
-                {
-                    acis.add(groupReadAci);
-                }
-
-                if (!acis.isEmpty())
-                {
-                    attributes.add(new Attribute("aci", 
-                            (String[]) acis.toArray(new String[acis.size()])));
-                }
-
-                List<String> members = new ArrayList<String>();
-                for (User<?> member : group.getUserMembers())
-                {
-                    DN memberDN = this.userPersist.getUserDN(member);
-                    members.add(memberDN.toNormalizedString());
-                }
-                for (Group gr : group.getGroupMembers())
+                
+                try
                 {
-                    DN grDN = getGroupDN(gr.getID());
-                    members.add(grDN.toNormalizedString());
+                    getInactiveGroup(group);
+                    return reactivateGroup(group);
                 }
-                if (!members.isEmpty())
+                catch (GroupNotFoundException e)
                 {
-                    attributes.add(new Attribute("uniquemember", 
-                        (String[]) members.toArray(new String[members.size()])));
+                    // ignore
                 }
-
-                AddRequest addRequest = 
-                        new AddRequest(getGroupDN(group.getID()), attributes);
-
-                addRequest.addControl(
-                        new ProxiedAuthorizationV2RequestControl(
-                                "dn:" + getSubjectDN().toNormalizedString()));
-
-                LDAPResult result = getConnection().add(addRequest);
+                
+                DN ownerDN = userPersist.getUserDN(group.getOwner());
+                
+                // add group to groups tree
+                LDAPResult result = addGroup(getGroupDN(group.getID()), 
+                                             group.getID(), ownerDN, 
+                                             group.description, 
+                                             group.getUserMembers(), 
+                                             group.getGroupMembers());
+                
+                // add group to admin groups tree
+                result = addGroup(getAdminGroupDN(group.getID()), 
+                                  group.getID(), ownerDN, 
+                                  group.description, 
+                                  group.getUserMembers(), 
+                                  group.getGroupMembers());
+                
                 try
                 {
                     return getGroup(group.getID());
@@ -309,10 +201,143 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             {
                 e.printStackTrace();
                 throw new RuntimeException(e);
+            } 
+        }
+    }
+    
+    private LDAPResult addGroup(final DN groupDN, final String groupID,
+                                final DN ownerDN, final String description, 
+                                final Set<User<? extends Principal>> users, 
+                                final Set<Group> groups)
+        throws UserNotFoundException, LDAPException
+    {
+        // add new group
+        List<Attribute> attributes = new ArrayList<Attribute>();
+        Attribute ownerAttribute = 
+                        new Attribute("owner", ownerDN.toNormalizedString());
+        attributes.add(ownerAttribute);
+        attributes.add(new Attribute("objectClass", "groupofuniquenames"));
+        attributes.add(new Attribute("cn", groupID));
+        
+        if (description != null)
+        {
+            attributes.add(new Attribute("description", description));
+        }
+
+        List<String> members = new ArrayList<String>();
+        for (User<? extends Principal> userMember : users)
+        {
+            DN memberDN = this.userPersist.getUserDN(userMember);
+            members.add(memberDN.toNormalizedString());
+        }
+        for (Group groupMember : groups)
+        {
+            DN memberDN = getGroupDN(groupMember.getID());
+            members.add(memberDN.toNormalizedString());
+        }
+        if (!members.isEmpty())
+        {
+            attributes.add(new Attribute("uniquemember", 
+                (String[]) members.toArray(new String[members.size()])));
+        }
+
+        AddRequest addRequest = new AddRequest(groupDN, attributes);
+
+        addRequest.addControl(
+                new ProxiedAuthorizationV2RequestControl(
+                        "dn:" + getSubjectDN().toNormalizedString()));
+
+        return getConnection().add(addRequest);
+    }
+    
+    private Group getInactiveGroup(final Group group)
+        throws AccessControlException, UserNotFoundException,
+        GroupNotFoundException
+    {
+        Group inactiveGroup;
+        try
+        {
+            inactiveGroup = getInactiveGroup(getGroupDN(group.getID())
+                    .toNormalizedString(), group.getID());
+
+            if (inactiveGroup == null)
+            {
+                return null;
             }
+
+            if (!group.getOwner().equals(inactiveGroup.getOwner()))
+            {
+                throw new AccessControlException(
+                        "Inactive group not owned be requestor");
+            }
+
+            Group inactiveAdminGroup = getInactiveGroup(
+                    getAdminGroupDN(group.getID()).toNormalizedString(),
+                    group.getID());
+
+            if (inactiveAdminGroup == null)
+            {
+                throw new RuntimeException(
+                        "BUG: adminGroup not found for group " + group.getID());
+            }
+
+            if (!group.getOwner().equals(inactiveAdminGroup.getOwner()))
+            {
+                throw new RuntimeException(
+                        "Bug: adminGroup owner doesn't match "
+                                + "group owner for group " + group.getID());
+            }
+            return inactiveGroup;
+        } 
+        catch (LDAPException e)
+        {
+            // TODO Auto-generated catch block
+            throw new RuntimeException("BUG: LDAP Exception: ", e);
+        }
+        
+        
+    }
+    
+    private Group getInactiveGroup(final String groupDN, final String groupID)
+        throws UserNotFoundException, LDAPException, GroupNotFoundException
+    {
+        Filter filter = Filter.createANDFilter(
+                Filter.createEqualityFilter("cn", groupID),
+                Filter.createEqualityFilter("nsaccountlock", "true"));
+
+        SearchRequest searchRequest = 
+                new SearchRequest(groupDN, SearchScope.SUB, filter, 
+                                  new String[] {"cn", "owner"});
+
+        searchRequest.addControl(
+                new ProxiedAuthorizationV2RequestControl("dn:" + 
+                        getSubjectDN().toNormalizedString()));
+
+        SearchResultEntry searchResult = 
+                getConnection().searchForEntry(searchRequest);
+        
+        if (searchResult == null)
+        {
+            String msg = "Inactive Group not found " + groupID;
+            logger.debug(msg);
+            throw new GroupNotFoundException(msg);
         }
+
+        String groupCN = searchResult.getAttributeValue("cn");
+        DN groupOwner = searchResult.getAttributeValueAsDN("owner");
+
+        User<X500Principal> owner = userPersist.getMember(groupOwner);
+
+        return new Group(groupCN, owner);
+    }
+    
+    private Group reactivateGroup(final Group group)
+        throws UserNotFoundException, LDAPException, TransientException, AccessControlException, GroupNotFoundException
+    {
+        return modifyGroup(group, true);
     }
 
+
     /**
      * Get the group with the given Group ID.
      * 
@@ -323,14 +348,50 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
      * @throws GroupNotFoundException If the group was not found.
      * @throws TransientException  If an temporary, unexpected problem occurred.
      */
-    public Group getGroup(String groupID)
+    public Group getGroup(final String groupID)
         throws GroupNotFoundException, TransientException,
                AccessControlException
     {
         return getGroup(groupID, true);
     }
+    
+    public Group getGroup(final String groupID, final boolean withMembers)
+        throws GroupNotFoundException, TransientException,
+               AccessControlException
+    {
+        Group group = getGroup(getGroupDN(groupID), groupID, true);
+        
+        Group adminGroup = getAdminGroup(getAdminGroupDN(groupID), groupID, 
+                                         true);
+        
+        group.getGroupAdmins().addAll(adminGroup.getGroupMembers());
+        group.getUserAdmins().addAll(adminGroup.getUserMembers());
+        return group;
+    }
+    
+    private Group getGroup(final DN groupDN, final String groupID, 
+                           final boolean withMembers)
+        throws GroupNotFoundException, TransientException, 
+               AccessControlException
+    {
+        String [] attributes = new String[] {"entrydn", "cn", "description", 
+                                             "owner", "uniquemember", 
+                                             "modifytimestamp"};
+        return getGroup(groupDN, groupID, withMembers, attributes);
+    }
+    
+    private Group getAdminGroup(final DN groupDN, final String groupID, 
+                           final boolean withMembers)
+        throws GroupNotFoundException, TransientException, 
+               AccessControlException
+    {
+        String [] attributes = new String[] {"entrydn", "cn", "owner",
+                                             "uniquemember"};
+        return getGroup(groupDN, groupID, withMembers, attributes);
+    }
 
-    private Group getGroup(String groupID, boolean withMembers)
+    private Group getGroup(final DN groupDN, final String groupID, 
+                           final boolean withMembers, String[] attributes)
         throws GroupNotFoundException, TransientException, 
                AccessControlException
     {
@@ -341,28 +402,26 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
                     Filter.createNOTFilter(
                         Filter.createEqualityFilter("nsaccountlock", "TRUE")));
             
-            SearchRequest searchRequest =  new SearchRequest(
-                    config.getGroupsDN(), SearchScope.SUB, 
-                    filter, new String[] {"entrydn", "cn", "description", 
-                                          "owner", "uniquemember", "aci", 
-                                          "modifytimestamp"});
+            SearchRequest searchRequest = 
+                    new SearchRequest(groupDN.toNormalizedString(), 
+                                      SearchScope.SUB, filter, attributes);
 
             searchRequest.addControl(
                     new ProxiedAuthorizationV2RequestControl("dn:" + 
                             getSubjectDN().toNormalizedString()));
 
-            SearchResultEntry group = 
+            SearchResultEntry searchResult = 
                     getConnection().searchForEntry(searchRequest);
-            if (group == null)
+            
+            if (searchResult == null)
             {
                 String msg = "Group not found " + groupID;
                 logger.debug(msg);
                 throw new GroupNotFoundException(groupID);
             }
-            String groupCN = group.getAttributeValue("cn");
-            DN groupOwner = group.getAttributeValueAsDN("owner");
-            Date lastModified = 
-                group.getAttributeValueAsDate("modifytimestamp");
+            
+            String groupCN = searchResult.getAttributeValue("cn");
+            DN groupOwner = searchResult.getAttributeValueAsDN("owner");
             
             User<X500Principal> owner;
             try
@@ -375,14 +434,22 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             }
             
             Group ldapGroup = new Group(groupCN, owner);
-            ldapGroup.description = group.getAttributeValue("description");
-            ldapGroup.lastModified = lastModified;
+            if (searchResult.hasAttribute("description"))
+            {
+                ldapGroup.description = 
+                        searchResult.getAttributeValue("description");
+            }
+            if (searchResult.hasAttribute("modifytimestamp"))
+            {
+                ldapGroup.lastModified = 
+                        searchResult.getAttributeValueAsDate("modifytimestamp");
+            }
 
             if (withMembers)
             {
-                if (group.getAttributeValues("uniquemember") != null)
+                if (searchResult.getAttributeValues("uniquemember") != null)
                 {
-                    for (String member : group
+                    for (String member : searchResult
                             .getAttributeValues("uniquemember"))
                     {
                         DN memberDN = new DN(member);
@@ -391,7 +458,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
                             User<X500Principal> user;
                             try
                             {
-                                user = userPersist.getMember(memberDN, false);
+                                user = userPersist.getMember(memberDN);
                             }
                             catch (UserNotFoundException e)
                             {
@@ -412,36 +479,6 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
                         }
                     }
                 }
-
-                // TODO not sure this is going to fly...
-                if (group.getAttributeValues("aci") != null)
-                {
-                    for (String aci : group.getAttributeValues("aci"))
-                    {
-                        if (aci.contains("Group Read"))
-                        {
-                            // TODO it's gotta be a better way to do this.
-                            String grRead = aci.substring(
-                                    aci.indexOf("ldap:///"));
-                            grRead = grRead.substring(grRead.indexOf("cn=") + 3,
-                                                      grRead.indexOf(','));
-
-                            Group groupRead = new Group(grRead.trim());
-                            ldapGroup.groupRead = groupRead;
-                        }
-                        else if (aci.contains("Group Write"))
-                        {
-                            // TODO it's gotta be a better way to do this.
-                            String grWrite = aci.substring(
-                                    aci.indexOf("ldap:///"));
-                            grWrite = grWrite.substring(grWrite.indexOf("cn=") + 3, 
-                                                    grWrite.indexOf(','));
-
-                            Group groupWrite = getGroup(grWrite.trim());
-                            ldapGroup.groupWrite = groupWrite;
-                        }
-                    }
-                }
             }
             
             return ldapGroup;
@@ -458,7 +495,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
     /**
      * Modify the given group.
      *
-     * @param group The group to update.
+     * @param group The group to update. It must be an existing group
      * 
      * @return The newly updated group.
      * 
@@ -471,141 +508,108 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         throws GroupNotFoundException, TransientException,
                AccessControlException, UserNotFoundException
     {
-        // check if group exists
-        Group oldGroup = getGroup(group.getID());
-        
-        return modifyGroup(group, oldGroup, null);
+        return modifyGroup(group, false); 
     }
     
-    private Group modifyGroup(Group newGroup, Group oldGroup,
-                             List<Modification> modifications)
+    private Group modifyGroup(final Group group,
+                              boolean withActivate)
         throws UserNotFoundException, TransientException,
-               AccessControlException
+               AccessControlException, GroupNotFoundException
     {
-        if (!newGroup.getProperties().isEmpty())
+        if (!group.getProperties().isEmpty())
         {
             throw new UnsupportedOperationException(
                     "Support for groups properties not available");
         }
-
-        List<Modification> modifs = new ArrayList<Modification>();
-        if (modifications != null)
-        {
-            modifs.addAll(modifications);
-        }
-
-        if (newGroup.description == null && oldGroup.description != null)
+        
+        // check if group exists
+        if (withActivate)
         {
-            modifs.add(new Modification(ModificationType.DELETE, 
-                                        "description"));
+            getInactiveGroup(group);
         }
-        else if (newGroup.description != null && oldGroup.description == null)
+        else
         {
-            modifs.add(new Modification(ModificationType.ADD, "description", 
-                                        newGroup.description));
+            getGroup(group.getID());
         }
-        else if (newGroup.description != null && oldGroup.description != null)
+
+        List<Modification> mods = new ArrayList<Modification>();
+        List<Modification> adminMods = new ArrayList<Modification>();
+        if (withActivate)
         {
-            modifs.add(new Modification(ModificationType.REPLACE, "description", 
-                                        newGroup.description));
+            mods.add(new Modification(ModificationType.DELETE, "nsaccountlock"));
+            adminMods.add(new Modification(ModificationType.DELETE, "nsaccountlock"));
         }
 
-        List<String> acis = new ArrayList<String>();
-        if (newGroup.groupRead != null)
+        if (group.description == null)
         {
-            if (newGroup.groupRead.equals(newGroup))
-            {
-                throw new IllegalArgumentException(
-                        "cyclical reference from groupRead to group");
-            }
-
-            DN readGrDN = getGroupDN(newGroup.groupRead.getID());
-            acis.add(GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, 
-                                            readGrDN.toNormalizedString()));
+            mods.add(new Modification(ModificationType.REPLACE, "description"));
         }
-
-        if (newGroup.groupWrite != null)
+        else
         {
-            if (newGroup.groupWrite.equals(newGroup))
-            {
-                throw new IllegalArgumentException(
-                        "cyclical reference from groupWrite to group");
-            }
-
-            DN writeGrDN = getGroupDN(newGroup.groupWrite.getID());
-            acis.add(GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, 
-                                             writeGrDN.toNormalizedString()));
+            mods.add(new Modification(ModificationType.REPLACE, "description", group.description));
         }
 
-        modifs.add(new Modification(ModificationType.REPLACE, "aci", (String[]) 
-                                    acis.toArray(new String[acis.size()])));
-
         List<String> newMembers = new ArrayList<String>();
-        for (User<?> member : newGroup.getUserMembers())
+        for (User<?> member : group.getUserMembers())
         {
-            if (!oldGroup.getUserMembers().remove(member))
+            DN memberDN;
+            try
             {
-                DN memberDN;
-                try
-                {
-                    memberDN = userPersist.getUserDN(member);
-                }
-                catch (LDAPException e)
-                {
-                    throw new UserNotFoundException(
-                            "User not found " + member.getUserID());
-                }
-                newMembers.add(memberDN.toNormalizedString());
+                memberDN = userPersist.getUserDN(member);
+            } 
+            catch (LDAPException e)
+            {
+                throw new UserNotFoundException("User not found "
+                        + member.getUserID());
             }
+            newMembers.add(memberDN.toNormalizedString());
         }
-        for (Group gr : newGroup.getGroupMembers())
+        for (Group gr : group.getGroupMembers())
         {
-            if (gr.equals(newGroup))
-            {
-                throw new IllegalArgumentException(
-                        "cyclical reference from group member to group");
-            }
-
-            if (!oldGroup.getGroupMembers().remove(gr))
-            {
                 DN grDN = getGroupDN(gr.getID());
                 newMembers.add(grDN.toNormalizedString());
-            }
         }
-        if (!newMembers.isEmpty())
-        {
-            modifs.add(new Modification(ModificationType.ADD, "uniquemember", 
-                (String[]) newMembers.toArray(new String[newMembers.size()])));
-        }
-
-        List<String> delMembers = new ArrayList<String>();
-        for (User<?> member : oldGroup.getUserMembers())
+        List<String> newAdmins = new ArrayList<String>();
+        for (User<?> member : group.getUserAdmins())
         {
             DN memberDN;
             try
             {
-                memberDN = this.userPersist.getUserDN(member);
+                memberDN = userPersist.getUserDN(member);
             }
             catch (LDAPException e)
             {
                 throw new UserNotFoundException(
                         "User not found " + member.getUserID());
             }
-            delMembers.add(memberDN.toNormalizedString());
+            newAdmins.add(memberDN.toNormalizedString());
         }
-        for (Group gr : oldGroup.getGroupMembers())
+        for (Group gr : group.getGroupAdmins())
         {
             DN grDN = getGroupDN(gr.getID());
-            delMembers.add(grDN.toNormalizedString());
+            newMembers.add(grDN.toNormalizedString());
+        }
+
+        mods.add(new Modification(ModificationType.REPLACE, "uniquemember", 
+                (String[]) newMembers.toArray(new String[newMembers.size()])));
+        adminMods.add(new Modification(ModificationType.REPLACE, "uniquemember", 
+                (String[]) newAdmins.toArray(new String[newAdmins.size()])));
+        
+        // modify admin group first
+        ModifyRequest modifyRequest = new ModifyRequest(getAdminGroupDN(group.getID()), adminMods);
+        try
+        {
+            modifyRequest.addControl(
+                    new ProxiedAuthorizationV2RequestControl(
+                            "dn:" + getSubjectDN().toNormalizedString()));
+            LDAPResult result = getConnection().modify(modifyRequest);
         }
-        if (!delMembers.isEmpty())
+        catch (LDAPException e1)
         {
-            modifs.add(new Modification(ModificationType.DELETE, "uniquemember",
-                (String[]) delMembers.toArray(new String[delMembers.size()])));
+            throw new RuntimeException("LDAP problem", e1);
         }
-
-        ModifyRequest modifyRequest = 
-                new ModifyRequest(getGroupDN(newGroup.getID()), modifs);
+        // modify the group itself now
+        modifyRequest = new ModifyRequest(getGroupDN(group.getID()), mods);
         try
         {
             modifyRequest.addControl(
@@ -619,11 +623,19 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         }
         try
         {
-            return getGroup(newGroup.getID());
+            if (withActivate)
+            {
+                return new ActivatedGroup(getGroup(group.getID()));
+            }
+            else
+            {
+                return getGroup(group.getID());
+            }
         }
         catch (GroupNotFoundException e)
         {
-            throw new RuntimeException("BUG: modified group not found");
+            throw new RuntimeException(
+                    "BUG: modified group not found (" + group.getID() + ")");
         }
     }
 
@@ -639,29 +651,37 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         throws GroupNotFoundException, TransientException,
                AccessControlException
     {
-        Group group = getGroup(groupID);
+        deleteGroup(getGroupDN(groupID), groupID, false);
+        deleteGroup(getAdminGroupDN(groupID), groupID, true);
+    }
+    
+    private void deleteGroup(final DN groupDN, final String groupID, 
+                             final boolean isAdmin)
+        throws GroupNotFoundException, TransientException,
+               AccessControlException
+    {
+        Group group = getGroup(groupDN, groupID, false);
         List<Modification> modifs = new ArrayList<Modification>();
         modifs.add(new Modification(ModificationType.ADD, "nsaccountlock", "true"));
         
-        if (group.description != null)
-        {
-            modifs.add(new Modification(ModificationType.DELETE, "description"));
-        }
-        
-        if (group.groupRead != null || 
-            group.groupWrite != null)
+        if (isAdmin)
         {
-            modifs.add(new Modification(ModificationType.DELETE, "aci"));
+            if (!group.getGroupAdmins().isEmpty() || 
+                !group.getUserAdmins().isEmpty())
+            {
+                modifs.add(new Modification(ModificationType.DELETE, "uniquemember"));
+            }
         }
-        
-        if (!group.getGroupMembers().isEmpty() || 
-            !group.getUserMembers().isEmpty())
+        else
         {
-            modifs.add(new Modification(ModificationType.DELETE, "uniquemember"));
+            if (!group.getGroupMembers().isEmpty() || 
+                !group.getUserMembers().isEmpty())
+            {
+                modifs.add(new Modification(ModificationType.DELETE, "uniquemember"));
+            }
         }
 
-        ModifyRequest modifyRequest = 
-                new ModifyRequest(getGroupDN(group.getID()), modifs);
+        ModifyRequest modifyRequest = new ModifyRequest(groupDN, modifs);
         try
         {
             modifyRequest.addControl(
@@ -726,7 +746,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         }
         else if (role == Role.RW)
         {
-            return getRWGroups(user, userDN, groupID);
+            return getAdminGroups(user, userDN, groupID);
         }
         throw new IllegalArgumentException("Unknown role " + role);
     }
@@ -793,17 +813,45 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             Collection<Group> groups = new ArrayList<Group>();
             if (userPersist.isMember(user.getUserID(), groupID))
             {
-                groups.add(getGroup(groupID));
+                groups.add(getGroup(groupID, false));
             }
             return groups;
         }
         else
         {
-            return userPersist.getUserGroups(user.getUserID());
+            try
+            {
+                Collection<Group> groups = 
+                        userPersist.getUserGroups(user.getUserID());
+
+                List<Filter> filters = new ArrayList<Filter>();
+                for (Group group : groups)
+                {
+                    filters.add(Filter.createEqualityFilter("cn", 
+                                                            group.getID()));
+                }
+
+                Filter filter = Filter.createORFilter(filters);
+                SearchRequest searchRequest =  new SearchRequest(
+                            config.getAdminGroupsDN(), SearchScope.SUB, filter, 
+                            "cn");
+            
+                SearchResult results = getConnection().search(searchRequest);
+                for (SearchResultEntry result : results.getSearchEntries())
+                {
+                    String groupName = result.getAttributeValue("cn");
+  
+                }
+                return groups;
+            }
+            catch (LDAPException e)
+            {
+                throw new TransientException(e.getDiagnosticMessage());
+            }
         }
     }
     
-    protected Collection<Group> getRWGroups(User<T> user, DN userDN,
+    protected Collection<Group> getAdminGroups(User<T> user, DN userDN,
                                             String groupID)
         throws TransientException, AccessControlException,
                GroupNotFoundException, UserNotFoundException
@@ -829,25 +877,25 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             List<Filter> filters = new ArrayList<Filter>();
             for (Group member : queryGroups)
             {
-                // Require both groupRead and groupWrite
-                if (member.groupRead != null && member.groupWrite != null)
-                {
-                    DN groupRead = getGroupDN(member.groupRead.getID());
-                    String groupReadAci = 
-                        GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, 
-                                           groupRead.toNormalizedString());
-                    DN groupWrite = getGroupDN(member.groupRead.getID());
-                    String groupWriteAci = 
-                        GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, 
-                                            groupWrite.toNormalizedString());
-                    System.out.println(groupReadAci);
-                    System.out.println(groupWriteAci);
-
-                    Filter filter = Filter.createANDFilter(
-                            Filter.createEqualityFilter("aci", groupReadAci),
-                            Filter.createEqualityFilter("aci", groupWriteAci));
-                    filters.add(filter);
-                }
+//                // Require both groupRead and groupWrite
+//                if (member.groupRead != null && member.groupWrite != null)
+//                {
+//                    DN groupRead = getGroupDN(member.groupRead.getID());
+//                    String groupReadAci = 
+//                        GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, 
+//                                           groupRead.toNormalizedString());
+//                    DN groupWrite = getGroupDN(member.groupRead.getID());
+//                    String groupWriteAci = 
+//                        GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, 
+//                                            groupWrite.toNormalizedString());
+//                    System.out.println(groupReadAci);
+//                    System.out.println(groupWriteAci);
+//
+//                    Filter filter = Filter.createANDFilter(
+//                            Filter.createEqualityFilter("aci", groupReadAci),
+//                            Filter.createEqualityFilter("aci", groupWriteAci));
+//                    filters.add(filter);
+//                }
             }
 
             Collection<Group> groups = new ArrayList<Group>();
@@ -895,119 +943,86 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         }
     }
     
-    protected Collection<Group> getRWGroups2(User<T> user, DN userDN, 
-                                             String groupID)
-        throws TransientException, AccessControlException,
-               GroupNotFoundException, UserNotFoundException
-    {
-        try
-        {
-            Collection<Group> groups = new ArrayList<Group>();
-            
-            Collection<Group> queryGroups =  new ArrayList<Group>();
-            if (groupID != null)
-            {
-                queryGroups.add(new Group(groupID, user));
-            }
-            else
-            {
-                // List of Groups the user belongs to.
-                queryGroups.addAll(getMemberGroups(user, userDN, groupID));
-            
-                // List of Groups the user owns;
-                queryGroups.addAll(getOwnerGroups(user, userDN, groupID));
-            }
-            
-            for (Group member : queryGroups)
-            {
-                // Require both groupRead and groupWrite
-                if (member.groupRead != null && member.groupWrite != null)
-                {
-                    DN groupRead = getGroupDN(member.groupRead.getID());
-                    String groupReadAci = 
-                            GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, 
-                                            groupRead.toNormalizedString());
-                    DN groupWrite = getGroupDN(member.groupWrite.getID());
-                    String groupWriteAci = 
-                            GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, 
-                                            groupWrite.toNormalizedString());
-
-                    Filter filter = Filter.createANDFilter(
-                            Filter.createEqualityFilter("aci", groupReadAci),
-                            Filter.createEqualityFilter("aci", groupWriteAci));
-
-                    SearchRequest searchRequest = new SearchRequest(
-                            config.getGroupsDN(), SearchScope.SUB, filter, 
-                            new String[] {"cn", "owner", "description", 
-                                          "modifytimestamp"});
-
-                    searchRequest.addControl(
-                            new ProxiedAuthorizationV2RequestControl("dn:" + 
-                                    getSubjectDN().toNormalizedString()));
-
-                    SearchResult results = getConnection().search(searchRequest);
-                    for (SearchResultEntry result : results.getSearchEntries())
-                    {
-                        String groupName = result.getAttributeValue("cn");
-                        DN ownerDN = result.getAttributeValueAsDN("owner");
-                        User<X500Principal> owner = userPersist.getMember(ownerDN);
-
-                        // Ignore existing illegal group names.
-                        try
-                        {
-                            Group group = new Group(groupName, owner);
-                            group.description = result.getAttributeValue("description");
-                            group.lastModified = 
-                                    result.getAttributeValueAsDate("modifytimestamp");
-                            groups.add(group);
-                        }
-                        catch (IllegalArgumentException ignore) { } 
-                    }
-                }
-            }
-            return groups;
-        }
-        catch (LDAPException e1)
-        {
-            // TODO check which LDAP exceptions are transient and which
-            // ones are
-            // access control
-            throw new TransientException("Error getting groups", e1);
-        }
-    }
-    
-    private Group getInactiveGroup(String groupID)
-        throws UserNotFoundException, GroupNotFoundException, LDAPException
-    {
-        Filter filter = Filter.createANDFilter(
-                Filter.createEqualityFilter("cn", groupID),
-                Filter.createEqualityFilter("nsaccountlock", "true"));
-
-        SearchRequest searchRequest =  new SearchRequest(
-                config.getGroupsDN(), SearchScope.SUB, 
-                filter, new String[] {"cn", "owner"});
-
-        searchRequest.addControl(
-                new ProxiedAuthorizationV2RequestControl("dn:" + 
-                        getSubjectDN().toNormalizedString()));
-
-        SearchResultEntry searchResult = 
-                getConnection().searchForEntry(searchRequest);
-        
-        if (searchResult == null)
-        {
-            String msg = "Inactive Group not found " + groupID;
-            logger.debug(msg);
-            throw new GroupNotFoundException(msg);
-        }
-
-        String groupCN = searchResult.getAttributeValue("cn");
-        DN groupOwner = searchResult.getAttributeValueAsDN("owner");
-
-        User<X500Principal> owner = userPersist.getMember(groupOwner);
-
-        return new Group(groupCN, owner);
-    }
+//    protected Collection<Group> getRWGroups2(User<T> user, DN userDN, 
+//                                             String groupID)
+//        throws TransientException, AccessControlException,
+//               GroupNotFoundException, UserNotFoundException
+//    {
+//        try
+//        {
+//            Collection<Group> groups = new ArrayList<Group>();
+//            
+//            Collection<Group> queryGroups =  new ArrayList<Group>();
+//            if (groupID != null)
+//            {
+//                queryGroups.add(new Group(groupID, user));
+//            }
+//            else
+//            {
+//                // List of Groups the user belongs to.
+//                queryGroups.addAll(getMemberGroups(user, userDN, groupID));
+//            
+//                // List of Groups the user owns;
+//                queryGroups.addAll(getOwnerGroups(user, userDN, groupID));
+//            }
+//            
+//            for (Group member : queryGroups)
+//            {
+//                // Require both groupRead and groupWrite
+//                if (member.groupRead != null && member.groupWrite != null)
+//                {
+//                    DN groupRead = getGroupDN(member.groupRead.getID());
+//                    String groupReadAci = 
+//                            GROUP_READ_ACI.replace(ACTUAL_GROUP_TOKEN, 
+//                                            groupRead.toNormalizedString());
+//                    DN groupWrite = getGroupDN(member.groupWrite.getID());
+//                    String groupWriteAci = 
+//                            GROUP_WRITE_ACI.replace(ACTUAL_GROUP_TOKEN, 
+//                                            groupWrite.toNormalizedString());
+//
+//                    Filter filter = Filter.createANDFilter(
+//                            Filter.createEqualityFilter("aci", groupReadAci),
+//                            Filter.createEqualityFilter("aci", groupWriteAci));
+//
+//                    SearchRequest searchRequest = new SearchRequest(
+//                            config.getGroupsDN(), SearchScope.SUB, filter, 
+//                            new String[] {"cn", "owner", "description", 
+//                                          "modifytimestamp"});
+//
+//                    searchRequest.addControl(
+//                            new ProxiedAuthorizationV2RequestControl("dn:" + 
+//                                    getSubjectDN().toNormalizedString()));
+//
+//                    SearchResult results = getConnection().search(searchRequest);
+//                    for (SearchResultEntry result : results.getSearchEntries())
+//                    {
+//                        String groupName = result.getAttributeValue("cn");
+//                        DN ownerDN = result.getAttributeValueAsDN("owner");
+//                        User<X500Principal> owner = userPersist.getMember(ownerDN);
+//
+//                        // Ignore existing illegal group names.
+//                        try
+//                        {
+//                            Group group = new Group(groupName, owner);
+//                            group.description = result.getAttributeValue("description");
+//                            group.lastModified = 
+//                                    result.getAttributeValueAsDate("modifytimestamp");
+//                            groups.add(group);
+//                        }
+//                        catch (IllegalArgumentException ignore) { } 
+//                    }
+//                }
+//            }
+//            return groups;
+//        }
+//        catch (LDAPException e)
+//        {
+//            // TODO check which LDAP exceptions are transient and which
+//            // ones are
+//            // access control
+//            throw new TransientException("Error getting groups", e);
+//        }
+//    }
 
     /**
      * Returns a group based on its LDAP DN. The returned group is bare
@@ -1019,35 +1034,82 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
      * @throws ca.nrc.cadc.ac.GroupNotFoundException
      * @throws ca.nrc.cadc.ac.UserNotFoundException
      */
-    protected Group getGroup(DN groupDN)
-        throws LDAPException, GroupNotFoundException, UserNotFoundException
-    {
-        SearchResultEntry searchResult = 
-                getConnection().getEntry(groupDN.toNormalizedString(),
-                                new String[] {"cn", "description"});
+//    protected Group getGroup(DN groupDN)
+//        throws LDAPException, GroupNotFoundException, UserNotFoundException
+//    {
+//        SearchResultEntry searchResult = 
+//                getConnection().getEntry(groupDN.toNormalizedString(),
+//                                new String[] {"cn", "description"});
+//
+//        if (searchResult == null)
+//        {
+//            String msg = "Group not found " + groupDN;
+//            logger.debug(msg);
+//            throw new GroupNotFoundException(groupDN.toNormalizedString());
+//        }
+//
+//        Group group = new Group(searchResult.getAttributeValue("cn"));
+//        group.description = searchResult.getAttributeValue("description");
+//        return group;
+//    }
 
-        if (searchResult == null)
+    /**
+     * 
+     * @param groupID
+     * @return 
+     */
+    protected DN getGroupDN(final String groupID)
+    {
+        try
         {
-            String msg = "Group not found " + groupDN;
-            logger.debug(msg);
-            throw new GroupNotFoundException(groupDN.toNormalizedString());
+            return new DN("cn=" + groupID + "," + config.getGroupsDN());
         }
-
-        Group group = new Group(searchResult.getAttributeValue("cn"));
-        group.description = searchResult.getAttributeValue("description");
-        return group;
+        catch (LDAPException e)
+        {
+        }
+        throw new IllegalArgumentException(groupID + " not a valid group ID");
     }
-
-    protected DN getGroupDN(String groupID)
+    
+    /**
+     * 
+     * @param groupID
+     * @return 
+     */
+    protected DN getAdminGroupDN(final String groupID)
     {
         try
         {
-            return new DN("cn=" + groupID + "," + config.getGroupsDN());
+            return new DN("cn=" + groupID + "," + config.getAdminGroupsDN());
         }
         catch (LDAPException e)
         {
         }
         throw new IllegalArgumentException(groupID + " not a valid group ID");
     }
+    
+    /**
+     * 
+     * @param owner
+     * @return
+     * @throws UserNotFoundException 
+     */
+    protected boolean isCreatorOwner(final User<? extends Principal> owner)
+        throws UserNotFoundException
+    {
+        try
+        {
+            User<X500Principal> subjectUser = 
+                    userPersist.getMember(getSubjectDN());
+            if (subjectUser.equals(owner))
+            {
+                return true;
+            }
+            return false;
+        }
+        catch (LDAPException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
 
 }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
index b58ade6f8eec036820fbff5e77c8d1640c964039..bb11f40ad609a5184f01357d47ffb5feb90eb3e7 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
@@ -68,12 +68,25 @@
  */
 package ca.nrc.cadc.ac.server.ldap;
 
+import java.security.AccessControlException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import javax.security.auth.x500.X500Principal;
+
+import org.apache.log4j.Logger;
+
 import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.PersonalDetails;
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import ca.nrc.cadc.auth.NumericPrincipal;
 import ca.nrc.cadc.net.TransientException;
+
 import com.unboundid.ldap.sdk.CompareRequest;
 import com.unboundid.ldap.sdk.CompareResult;
 import com.unboundid.ldap.sdk.DN;
@@ -82,30 +95,39 @@ import com.unboundid.ldap.sdk.LDAPException;
 import com.unboundid.ldap.sdk.SearchRequest;
 import com.unboundid.ldap.sdk.SearchResultEntry;
 import com.unboundid.ldap.sdk.SearchScope;
-import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV1RequestControl;
 import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl;
-import java.security.AccessControlException;
-import java.security.Principal;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import javax.security.auth.x500.X500Principal;
-import org.apache.log4j.Logger;
 
 public class LdapUserDAO<T extends Principal> extends LdapDAO
 {
     private static final Logger logger = Logger.getLogger(LdapUserDAO.class);
 
     // Map of identity type to LDAP attribute
-    private Map<Class<?>, String> attribType = new HashMap<Class<?>, String>();
+    private Map<Class<?>, String> userLdapAttrib = new HashMap<Class<?>, String>();
+    
+    // User attributes returned to the GMS
+    private static final String LDAP_FNAME = "givenname";
+    private static final String LDAP_LNAME = "sn";
+    //TODO to add the rest
+    private String[] userAttribs = new String[]{LDAP_FNAME, LDAP_LNAME};
+    private String[] memberAttribs = new String[]{LDAP_FNAME, LDAP_LNAME};
 
     public LdapUserDAO(LdapConfig config)
     {
         super(config);
-        this.attribType.put(HttpPrincipal.class, "cn");
-        this.attribType.put(X500Principal.class, "distinguishedname");
-        this.attribType.put(NumericPrincipal.class, "entryid");
+        this.userLdapAttrib.put(HttpPrincipal.class, "uid");
+        this.userLdapAttrib.put(X500Principal.class, "distinguishedname");
+        
+        // add the id attributes to user and member attributes
+        String[] princs = userLdapAttrib.values().toArray(new String[userLdapAttrib.values().size()]);
+        String[] tmp = new String[userAttribs.length + princs.length];
+        System.arraycopy(princs, 0, tmp, 0, princs.length);
+        System.arraycopy(userAttribs, 0, tmp, princs.length, userAttribs.length);
+        userAttribs = tmp;
+        
+        tmp = new String[memberAttribs.length + princs.length];
+        System.arraycopy(princs, 0, tmp, 0, princs.length);
+        System.arraycopy(memberAttribs, 0, tmp, princs.length, memberAttribs.length);
+        memberAttribs = tmp;
     }
 
     /**
@@ -122,7 +144,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
     public User<T> getUser(T userID)
         throws UserNotFoundException, TransientException, AccessControlException
     {
-        String searchField = (String) attribType.get(userID.getClass());
+        String searchField = (String) userLdapAttrib.get(userID.getClass());
         if (searchField == null)
         {
             throw new IllegalArgumentException(
@@ -135,8 +157,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
         try
         {
             SearchRequest searchRequest = new SearchRequest(config.getUsersDN(), 
-                    SearchScope.SUB, searchField, 
-                    new String[] {"cn", "entryid", "entrydn", "dn"});
+                    SearchScope.SUB, searchField, userAttribs);
  
             searchRequest.addControl(
                     new ProxiedAuthorizationV2RequestControl("dn:" + 
@@ -157,12 +178,13 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
         }
         User<T> user = new User<T>(userID);
         user.getIdentities().add(
-                new HttpPrincipal(searchResult.getAttributeValue("cn")));
-
-        user.getIdentities().add(
-                new NumericPrincipal(
-                        searchResult.getAttributeValueAsInteger("entryid")));
-
+                new HttpPrincipal(searchResult.getAttributeValue(userLdapAttrib
+                        .get(HttpPrincipal.class))));
+        
+        String fname = searchResult.getAttributeValue(LDAP_FNAME);
+        String lname = searchResult.getAttributeValue(LDAP_LNAME);
+        user.details.add(new PersonalDetails(fname, lname));
+        //TODO populate user with the other returned personal or posix attributes
         return user;
     }   
 
@@ -182,14 +204,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
     {
         try
         {
-            String searchField = (String) attribType.get(userID.getClass());
+            String searchField = (String) userLdapAttrib.get(userID.getClass());
             if (searchField == null)
             {
                 throw new IllegalArgumentException(
                         "Unsupported principal type " + userID.getClass());
             }
 
-            User user = getUser(userID);
+            User<T> user = getUser(userID);
             Filter filter = Filter.createANDFilter(
                         Filter.createEqualityFilter(searchField, 
                                                     user.getUserID().getName()),
@@ -256,14 +278,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
     {
         try
         {
-            String searchField = (String) attribType.get(userID.getClass());
+            String searchField = (String) userLdapAttrib.get(userID.getClass());
             if (searchField == null)
             {
                 throw new IllegalArgumentException(
                         "Unsupported principal type " + userID.getClass());
             }
 
-            User user = getUser(userID);
+            User<T> user = getUser(userID);
             Filter filter = Filter.createANDFilter(
                         Filter.createEqualityFilter(searchField, 
                                                     user.getUserID().getName()),
@@ -301,14 +323,14 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
     {
         try
         {
-            String searchField = (String) attribType.get(userID.getClass());
+            String searchField = (String) userLdapAttrib.get(userID.getClass());
             if (searchField == null)
             {
                 throw new IllegalArgumentException(
                         "Unsupported principal type " + userID.getClass());
             }
 
-            User user = getUser(userID);
+            User<T> user = getUser(userID);
             DN userDN = getUserDN(user);
 
             CompareRequest compareRequest = 
@@ -333,16 +355,16 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
     }
     
     /**
-     * Returns a member user identified by the X500Principal only.
+     * Returns a member user identified by the X500Principal only. The
+     * returned object has the fields required by the GMS.
+     * Note that this method binds as a proxy user and not as the 
+     * subject.
      * @param userDN
-     * @param bindAsSubject - true if Ldap commands executed as subject 
-     * (proxy authorization) or false if they are executed as the user
-     * in the connection.
      * @return
      * @throws UserNotFoundException
      * @throws LDAPException
      */
-    User<X500Principal> getMember(DN userDN, boolean bindAsSubject)
+    User<X500Principal> getMember(DN userDN)
         throws UserNotFoundException, LDAPException
     {
         Filter filter = 
@@ -351,50 +373,37 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
         
         SearchRequest searchRequest = 
                 new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, 
-                                  filter, 
-                                  (String[]) this.attribType.values().toArray(
-                                  new String[this.attribType.values().size()]));
-        
-        if (bindAsSubject)
-        {
-        	searchRequest.addControl(
-        				new ProxiedAuthorizationV2RequestControl("dn:" + 
-        						getSubjectDN().toNormalizedString()));
-        }
+                                  filter, memberAttribs);
         
         SearchResultEntry searchResult = 
                 getConnection().searchForEntry(searchRequest);
 
         if (searchResult == null)
         {
-            String msg = "User not found " + userDN;
+            String msg = "Member not found " + userDN;
             logger.debug(msg);
             throw new UserNotFoundException(msg);
         }
         User<X500Principal> user = new User<X500Principal>(
                 new X500Principal(searchResult.getAttributeValue(
-                        (String) attribType.get(X500Principal.class))));
-
+                        (String) userLdapAttrib.get(X500Principal.class))));
+        String princ = searchResult.getAttributeValue(
+                (String) userLdapAttrib.get(HttpPrincipal.class));
+        if (princ != null)
+        {
+            user.getIdentities().add(new HttpPrincipal(princ));
+        }
+        String fname = searchResult.getAttributeValue(LDAP_FNAME);
+        String lname = searchResult.getAttributeValue(LDAP_LNAME);
+        user.details.add(new PersonalDetails(fname, lname));
         return user;
     }
     
-    /**
-     * Returns a member user identified by the X500Principal only.
-     * @param userDN
-     * @return
-     * @throws UserNotFoundException
-     * @throws LDAPException
-     */
-    User<X500Principal> getMember(DN userDN)
-        throws UserNotFoundException, LDAPException
-    {
-        return getMember(userDN, true);
-    }
 
     DN getUserDN(User<? extends Principal> user)
         throws LDAPException, UserNotFoundException
     {
-        String searchField = (String) attribType.get(user.getUserID().getClass());
+        String searchField = (String) userLdapAttrib.get(user.getUserID().getClass());
         if (searchField == null)
         {
             throw new IllegalArgumentException(
@@ -408,9 +417,9 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
                 new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, 
                                  searchField, new String[] {"entrydn"});
         
-        searchRequest.addControl(
-                    new ProxiedAuthorizationV2RequestControl("dn:" + 
-                            getSubjectDN().toNormalizedString()));
+//        searchRequest.addControl(
+//                    new ProxiedAuthorizationV2RequestControl("dn:" + 
+//                            getSubjectDN().toNormalizedString()));
 
         SearchResultEntry searchResult = 
                 getConnection().searchForEntry(searchRequest);
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java
index 9a2c2caddd06d278b8fd4ff6e13e7047e55019a0..a79aa1a456ba2e629c331b1143f5d08900dec9ea 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java
@@ -89,10 +89,11 @@ public class LdapDAOTest
     static int port = 389;
     static String adminDN = "uid=webproxy,ou=WebProxy,ou=topologymanagement,o=netscaperoot";
     static String adminPW = "go4it";
-    static String userBaseDN = "ou=Users,ou=ds,dc=canfartest,dc=net";
-    static String groupBaseDN = "ou=Groups,ou=ds,dc=canfartest,dc=net";
+    static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net";
+    static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net";
+    static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net";
     
-    LdapConfig config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN);
+    LdapConfig config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN);
     
     @Test
     public void testLdapBindConnection() throws Exception
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java
index 94081f73add469471820754ac17ba6c20d5e452d..922432fb10ded92b85ab633190739bb5aa37479d 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java
@@ -69,10 +69,11 @@ public class LdapGroupDAOTest
     static int port = 389;
     static String adminDN = "uid=webproxy,ou=webproxy,ou=topologymanagement,o=netscaperoot";
     static String adminPW = "go4it";
-    static String userBaseDN = "ou=Users,ou=ds,dc=canfartest,dc=net";
-    static String groupBaseDN = "ou=Groups,ou=ds,dc=canfartest,dc=net";
-    //static String userBaseDN = "ou=Users,ou=ds,dc=canfar,dc=net";
-    //static String groupBaseDN = "ou=Groups,ou=ds,dc=canfar,dc=net";
+    static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net";
+    static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net";
+    static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net";
+    //static String usersDN = "ou=Users,ou=ds,dc=canfar,dc=net";
+    //static String groupsDN = "ou=Groups,ou=ds,dc=canfar,dc=net";
     
     static String daoTestDN1 = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca";
     static String daoTestDN2 = "cn=cadcdaotest2,ou=cadc,o=hia,c=ca";
@@ -115,7 +116,7 @@ public class LdapGroupDAOTest
         anonSubject = new Subject();
         anonSubject.getPrincipals().add(unknownUser.getUserID());
     
-        config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN);
+        config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN);
     }
 
     LdapGroupDAO<X500Principal> getGroupDAO()
@@ -158,24 +159,6 @@ public class LdapGroupDAOTest
                     actualGroup = getGroupDAO().modifyGroup(expectGroup);
                     assertGroupsEqual(expectGroup, actualGroup);
 
-                    // groupRead
-                    expectGroup.groupRead = otherGroup;
-                    actualGroup = getGroupDAO().modifyGroup(expectGroup);
-                    assertGroupsEqual(expectGroup, actualGroup);
-                    
-                    expectGroup.groupRead = null;
-                    actualGroup = getGroupDAO().modifyGroup(expectGroup);
-                    assertGroupsEqual(expectGroup, actualGroup);
-
-                    // groupWrite
-                    expectGroup.groupWrite = otherGroup;
-                    actualGroup = getGroupDAO().modifyGroup(expectGroup);
-                    assertGroupsEqual(expectGroup, actualGroup);
-                    
-                    expectGroup.groupWrite = null;
-                    actualGroup = getGroupDAO().modifyGroup(expectGroup);
-                    assertGroupsEqual(expectGroup, actualGroup);
-
                     // userMembers
                     expectGroup.getUserMembers().add(daoTestUser2);
                     actualGroup = getGroupDAO().modifyGroup(expectGroup);
@@ -196,8 +179,6 @@ public class LdapGroupDAOTest
                     
                     // delete the group
                     expectGroup.description = "Happy testing";
-                    expectGroup.groupRead = otherGroup;
-                    expectGroup.groupWrite = otherGroup;
                     expectGroup.getUserMembers().add(daoTestUser2);
                     expectGroup.getGroupMembers().add(otherGroup);
                     
@@ -398,9 +379,9 @@ public class LdapGroupDAOTest
                 {                    
                     getGroupDAO().addGroup(new Group("foo", unknownUser));
                     fail("addGroup with unknown user should throw " + 
-                         "UserNotFoundException");
+                         "AccessControlException");
                 }
-                catch (UserNotFoundException ignore) {}
+                catch (AccessControlException ignore) {}
                 
                 Group group = getGroupDAO().addGroup(new Group(getGroupID(), 
                                                      daoTestUser1));
@@ -476,15 +457,6 @@ public class LdapGroupDAOTest
             public Object run() throws Exception
             {
                 getGroupDAO().addGroup(new Group(groupID, daoTestUser1));
-                
-//                try
-//                {
-//                    getGroupDAO().modifyGroup(new Group(groupID, unknownUser));
-//                    fail("modifyGroup with unknown user should throw " + 
-//                         "UserNotFoundException");
-//                }
-//                catch (UserNotFoundException ignore) {}
-                
                 try
                 {
                     getGroupDAO().modifyGroup(new Group("foo", daoTestUser1));
@@ -651,9 +623,6 @@ public class LdapGroupDAOTest
         {
             assertTrue(gr2.getUserMembers().contains(user));
         }
-        assertEquals(gr1.groupRead, gr2.groupRead);
-        assertEquals(gr1.groupWrite, gr2.groupWrite);
-        assertEquals(gr1.groupWrite, gr2.groupWrite);
         assertEquals(gr1.getProperties(), gr2.getProperties());
         for (GroupProperty prop : gr1.getProperties())
         {
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java
index a742fe5b6d586fa00596cc5df4939262c9b54188..f02c0f12c21689c61b6625bac768e3fd91efe835 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java
@@ -68,22 +68,33 @@
  */
 package ca.nrc.cadc.ac.server.ldap;
 
-import ca.nrc.cadc.ac.Group;
-import ca.nrc.cadc.ac.User;
-import ca.nrc.cadc.util.Log4jInit;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.security.Principal;
 import java.security.PrivilegedExceptionAction;
 import java.util.Collection;
+
 import javax.security.auth.Subject;
 import javax.security.auth.x500.X500Principal;
+
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.PersonalDetails;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserDetails;
+import ca.nrc.cadc.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.NumericPrincipal;
+import ca.nrc.cadc.util.Log4jInit;
+
+import com.unboundid.ldap.sdk.DN;
+
 /**
  *
  * @author jburke
@@ -96,12 +107,15 @@ public class LdapUserDAOTest
     static int port = 389;
     static String adminDN = "uid=webproxy,ou=Webproxy,ou=topologymanagement,o=netscaperoot";
     static String adminPW = "go4it";
-    static String userBaseDN = "ou=Users,ou=ds,dc=canfartest,dc=net";
-    static String groupBaseDN = "ou=Groups,ou=ds,dc=canfartest,dc=net";
+    static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net";
+    static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net";
+    static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net";
 //    static String userBaseDN = "ou=Users,ou=ds,dc=canfar,dc=net";
 //    static String groupBaseDN = "ou=Groups,ou=ds,dc=canfar,dc=net";
     
-    static final String testUserDN = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca";
+    static final String testUserX509DN = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca";
+    static final String testUserDN = "uid=cadcdaotest1," + usersDN;
+    
     
     static User<X500Principal> testUser;
     static LdapConfig config;
@@ -112,9 +126,12 @@ public class LdapUserDAOTest
     {
         Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG);
         
-        testUser = new User<X500Principal>(new X500Principal(testUserDN));
+        testUser = new User<X500Principal>(new X500Principal(testUserX509DN));
     
-        config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN);
+        config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN);
+        
+        testUser.details.add(new PersonalDetails("CADC", "DAOTest1"));
+        testUser.getIdentities().add(new HttpPrincipal("CadcDaoTest1"));        
     }
 
     LdapUserDAO<X500Principal> getUserDAO()
@@ -138,8 +155,8 @@ public class LdapUserDAOTest
             {
                 try
                 {
-                    User actual = getUserDAO().getUser(testUser.getUserID());
-                    assertEquals(testUser, actual);
+                    User<X500Principal> actual = getUserDAO().getUser(testUser.getUserID());
+                    check(testUser, actual);
                     
                     return null;
                 }
@@ -149,6 +166,7 @@ public class LdapUserDAOTest
                 }
             }
         });
+
     }
 
     /**
@@ -216,4 +234,90 @@ public class LdapUserDAOTest
         });
     }
     
+    
+    /**
+     * Test of getMember.
+     */
+    @Test
+    public void testGetMember() throws Exception
+    {
+        Subject subject = new Subject();
+        subject.getPrincipals().add(testUser.getUserID());
+
+        // do everything as owner
+        Subject.doAs(subject, new PrivilegedExceptionAction<Object>()
+        {
+            public Object run() throws Exception
+            {
+                try
+                {   
+                    User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN));
+                    check(testUser, actual);
+                    return null;
+                }
+                catch (Exception e)
+                {
+                    throw new Exception("Problems", e);
+                }
+            }
+        });
+        
+        
+        // should also work as a different user
+        subject = new Subject();
+        subject.getPrincipals().add(new HttpPrincipal("CadcDaoTest2"));
+
+        // do everything as owner
+        Subject.doAs(subject, new PrivilegedExceptionAction<Object>()
+        {
+            public Object run() throws Exception
+            {
+                try
+                {   
+                    User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN));
+                    check(testUser, actual);
+                    return null;
+                }
+                catch (Exception e)
+                {
+                    throw new Exception("Problems", e);
+                }
+            }
+        });
+    }
+    
+    
+    private static void check(final User<? extends Principal> user1, final User<? extends Principal> user2)
+    {
+        assertEquals(user1, user2);
+        assertEquals(user1.details, user2.details);
+        assertEquals(user1.details.size(), user2.details.size());
+        assertEquals(user1.getIdentities(), user2.getIdentities());
+        for(UserDetails d1 : user1.details)
+        {
+            assertTrue(user2.details.contains(d1));
+            if(d1 instanceof PersonalDetails)
+            {
+                PersonalDetails pd1 = (PersonalDetails)d1;
+                boolean found = false;
+                for(UserDetails d2 : user2.details)
+                {
+                    if(d2 instanceof PersonalDetails)
+                    {
+                        PersonalDetails pd2 = (PersonalDetails)d2;
+                        assertEquals(pd1, pd2); // already done in contains above but just in case
+                        assertEquals(pd1.address, pd2.address);
+                        assertEquals(pd1.city, pd2.city);
+                        assertEquals(pd1.country, pd2.country);
+                        assertEquals(pd1.email, pd2.email);
+                        assertEquals(pd1.institute, pd2.institute);
+                        found = true;
+                    }
+                    assertTrue(found);
+                }
+            }
+        }
+        
+    }
+    
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java
index 189088c9de5af0f082e88032c846ae859c18c4ba..22e445ede8750b77db6dd5f0bc07db03420531db 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ActivatedGroup.java
@@ -68,19 +68,18 @@
  */
 package ca.nrc.cadc.ac;
 
-import java.security.Principal;
 
 public class ActivatedGroup extends Group
 {
-
-    public ActivatedGroup(String groupID)
-    {
-        super(groupID);
-    }
-    
-    public ActivatedGroup(String groupID, User<? extends Principal> owner)
+    public ActivatedGroup(Group group)
     {
-        super(groupID, owner);
+        super(group.getID(), group.getOwner());
+        this.description = group.description;
+        this.properties = group.getProperties();
+        this.lastModified = group.lastModified;
+        this.getUserMembers().addAll(group.getUserMembers());
+        this.getGroupMembers().addAll(group.getGroupMembers());
+        this.getUserAdmins().addAll(group.getUserAdmins());
+        this.getGroupAdmins().addAll(group.getGroupAdmins());
     }
-
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java
index 5172ddb934629adcc018d4cdccc02dbed47e9912..c8e8048ee08a45712e47a93380c8441dd42f650a 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java
@@ -88,21 +88,14 @@ public class Group
     // group's group members
     private Set<Group> groupMembers = new HashSet<Group>();
     
-    public String description;
-    public Date lastModified;
+    // group's user admins
+    private Set<User<? extends Principal>> userAdmins = new HashSet<User<? extends Principal>>();
     
-    // Access Control properties
-    /**
-     * group that can read details of this group
-     * Note: this class does not enforce any access control rules
-     */
-    public Group groupRead;
+    // group's group admins
+    private Set<Group> groupAdmins = new HashSet<Group>();
     
-    /**
-     * group that can read and write details of this group
-     * Note: this class does not enforce any access control rules
-     */
-    public Group groupWrite;
+    public String description;
+    public Date lastModified;
     
     /**
      * Ctor.
@@ -186,6 +179,24 @@ public class Group
     {
         return groupMembers;
     }
+    
+    /**
+     * 
+     * @return individual user admins of this group
+     */
+    public Set<User<? extends Principal>> getUserAdmins()
+    {
+        return userAdmins;
+    }
+
+    /**
+     * 
+     * @return group admins of this group
+     */
+    public Set<Group> getGroupAdmins()
+    {
+        return groupAdmins;
+    }
 
     /* (non-Javadoc)
      * @see java.lang.Object#hashCode()
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
index 4b666bf54f3cb023faaac7ae1f26f630465d8bf0..8061f48773eeb3913a4b8020aba06af37e703fed 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
@@ -263,38 +263,37 @@ public class GroupReader
 
         }
 
-        // groupRead
-        Element groupReadElement = groupElement.getChild("groupRead");
-        if (groupReadElement != null)
+        // userMembers
+        Element userMembersElement = groupElement.getChild("userMembers");
+        if (userMembersElement != null)
         {
-            Element groupReadGroupElement = groupReadElement.getChild("group");
-            if (groupReadGroupElement != null)
+            List<Element> userElements = userMembersElement.getChildren("user");
+            for (Element userMember : userElements)
             {
-                group.groupRead = parseGroup(groupReadGroupElement);
+                group.getUserMembers().add(UserReader.parseUser(userMember));
             }
-
         }
-
-        // groupWrite
-        Element groupWriteElement = groupElement.getChild("groupWrite");
-        if (groupWriteElement != null)
+        
+        // groupAdmins
+        Element groupAdminsElement = groupElement.getChild("groupAdmins");
+        if (groupAdminsElement != null)
         {
-            Element groupWriteGroupElement = groupWriteElement.getChild("group");
-            if (groupWriteGroupElement != null)
+            List<Element> groupElements = groupAdminsElement.getChildren("group");
+            for (Element groupMember : groupElements)
             {
-                group.groupWrite = parseGroup(groupWriteGroupElement);
+                group.getGroupAdmins().add(parseGroup(groupMember));
             }
 
         }
 
-        // userMembers
-        Element userMembersElement = groupElement.getChild("userMembers");
-        if (userMembersElement != null)
+        // userAdmins
+        Element userAdminsElement = groupElement.getChild("userAdmins");
+        if (userAdminsElement != null)
         {
-            List<Element> userElements = userMembersElement.getChildren("user");
+            List<Element> userElements = userAdminsElement.getChildren("user");
             for (Element userMember : userElements)
             {
-                group.getUserMembers().add(UserReader.parseUser(userMember));
+                group.getUserAdmins().add(UserReader.parseUser(userMember));
             }
         }
 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
index 3c4e54471dd9e156be95c2c8aa7b24fa09c4a1bc..93fcd13c61f9a5492beb3468049997cbeb908b25 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
@@ -213,22 +213,6 @@ public class GroupWriter
                 groupElement.addContent(groupMembersElement);
             }
 
-            // Group groupRead.
-            if (group.groupRead != null)
-            {
-                Element groupReadElement = new Element("groupRead");
-                groupReadElement.addContent(getGroupElement(group.groupRead, false));
-                groupElement.addContent(groupReadElement);
-            }
-
-            // Group groupWrite.
-            if (group.groupWrite != null)
-            {
-                Element groupWriteElement = new Element("groupWrite");
-                groupWriteElement.addContent(getGroupElement(group.groupWrite, false));
-                groupElement.addContent(groupWriteElement);
-            }
-
             // Group userMembers
             if ((group.getUserMembers() != null) && (!group.getUserMembers().isEmpty()))
             {
@@ -239,6 +223,28 @@ public class GroupWriter
                 }
                 groupElement.addContent(userMembersElement);
             }
+            
+            // Group groupAdmins.
+            if ((group.getGroupAdmins() != null) && (!group.getGroupAdmins().isEmpty()))
+            {
+                Element groupAdminsElement = new Element("groupAdmins");
+                for (Group groupMember : group.getGroupAdmins())
+                {
+                    groupAdminsElement.addContent(getGroupElement(groupMember, false));
+                }
+                groupElement.addContent(groupAdminsElement);
+            }
+
+            // Group userAdmins
+            if ((group.getUserAdmins() != null) && (!group.getUserAdmins().isEmpty()))
+            {
+                Element userAdminsElement = new Element("userAdmins");
+                for (User<? extends Principal> userMember : group.getUserAdmins())
+                {
+                    userAdminsElement.addContent(UserWriter.getUserElement(userMember));
+                }
+                groupElement.addContent(userAdminsElement);
+            }
         }
 
         return groupElement;
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java
index 83675c64da17bab6cdc482c4da213357340d82da..c54f6ee56d0787551e5c3fb7c45a1fad85b67523 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java
@@ -85,8 +85,8 @@ import org.apache.log4j.Logger;
 import org.junit.Test;
 
 import ca.nrc.cadc.auth.HttpPrincipal;
-import ca.nrc.cadc.auth.NumericPrincipal;
 import ca.nrc.cadc.auth.OpenIdPrincipal;
+import static org.junit.Assert.assertTrue;
 
 /**
  *
@@ -161,15 +161,15 @@ public class GroupReaderWriterTest
         expected.lastModified = new Date();
         expected.properties.add(new GroupProperty("key", "value", true));
         
-        Group readGroup = new Group("read", new User<Principal>(new X500Principal("cn=foo,o=ca")));
-        Group writeGroup = new Group("write", new User<Principal>(new NumericPrincipal(123l)));
         Group groupMember = new Group("member", new User<Principal>(new OpenIdPrincipal("bar")));
         User<Principal> userMember = new User<Principal>(new HttpPrincipal("baz"));
+        Group groupAdmin = new Group("admin", new User<Principal>(new X500Principal("cn=foo,o=ca")));
+        User<Principal> userAdmin = new User<Principal>(new HttpPrincipal("admin"));
         
-        expected.groupRead = readGroup;
-        expected.groupWrite = writeGroup;
         expected.getGroupMembers().add(groupMember);
         expected.getUserMembers().add(userMember);
+        expected.getGroupAdmins().add(groupAdmin);
+        expected.getUserAdmins().add(userAdmin);
         
         StringBuilder xml = new StringBuilder();
         GroupWriter.write(expected, xml);
@@ -181,8 +181,6 @@ public class GroupReaderWriterTest
         assertEquals(expected.description, actual.description);
         assertEquals(expected.lastModified, actual.lastModified);
         assertEquals(expected.getProperties(), actual.getProperties());
-        assertEquals(expected.groupRead, actual.groupRead);
-        assertEquals(expected.groupWrite, actual.groupWrite);
         assertEquals(expected.getGroupMembers(), actual.getGroupMembers());
         assertEquals(expected.getUserMembers(), actual.getUserMembers());
     }
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
index af09cf893e6144d347dac2eed526d9496ecf1361..6451d53418baf3c5fa1099b97b738c508f27b70b 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
@@ -115,17 +115,15 @@ public class GroupTest
         assertEquals(group3.hashCode(), group4.hashCode());
         assertEquals(group3,group4);
         
-        group3.description = "Test group";
+        group4.getUserAdmins().add(user);
         assertEquals(group3.hashCode(), group4.hashCode());
         assertEquals(group3,group4);
         
-        // group read and write equality tests     
-        group3.groupRead = group4;
+        group3.getGroupAdmins().add(group4);
         assertEquals(group3.hashCode(), group4.hashCode());
         assertEquals(group3,group4);
         
-        // group write equality tests
-        group3.groupWrite = group4;
+        group3.description = "Test group";
         assertEquals(group3.hashCode(), group4.hashCode());
         assertEquals(group3,group4);