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..31eff4b1c1476d772ee21375a290faecf8f15fb0 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
@@ -151,22 +151,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 +164,29 @@ 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())
-                {
-                    DN grDN = getGroupDN(gr.getID());
-                    members.add(grDN.toNormalizedString());
-                }
-                if (!members.isEmpty())
+                
+                Group inactiveGroup = getInactiveGroup(group.getID());
+                if (inactiveGroup != null)
                 {
-                    attributes.add(new Attribute("uniquemember", 
-                        (String[]) members.toArray(new String[members.size()])));
+                    return reactiveGroup(group, inactiveGroup);
                 }
-
-                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());
+                Attribute ownerAttribute = 
+                        new Attribute("owner", ownerDN.toNormalizedString());
+                
+                // add group to groups tree
+                LDAPResult result = addGroup(group, ownerAttribute);
+                
+                // add group to admin groups tree
+                result = addAdminGroup(group, ownerAttribute);
+                
                 try
                 {
                     return getGroup(group.getID());
@@ -412,36 +303,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;
@@ -510,36 +371,6 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
                                         newGroup.description));
         }
 
-        List<String> acis = new ArrayList<String>();
-        if (newGroup.groupRead != 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()));
-        }
-
-        if (newGroup.groupWrite != null)
-        {
-            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()));
-        }
-
-        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())
         {
@@ -648,12 +479,6 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             modifs.add(new Modification(ModificationType.DELETE, "description"));
         }
         
-        if (group.groupRead != null || 
-            group.groupWrite != null)
-        {
-            modifs.add(new Modification(ModificationType.DELETE, "aci"));
-        }
-        
         if (!group.getGroupMembers().isEmpty() || 
             !group.getUserMembers().isEmpty())
         {
@@ -829,25 +654,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,89 +720,89 @@ 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);
-        }
-    }
+//    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);
+//        }
+//    }
     
-    private Group getInactiveGroup(String groupID)
-        throws UserNotFoundException, GroupNotFoundException, LDAPException
+    protected Group getInactiveGroup(final String groupID)
+        throws UserNotFoundException, LDAPException
     {
         Filter filter = Filter.createANDFilter(
                 Filter.createEqualityFilter("cn", groupID),
@@ -998,7 +823,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         {
             String msg = "Inactive Group not found " + groupID;
             logger.debug(msg);
-            throw new GroupNotFoundException(msg);
+            return null;
         }
 
         String groupCN = searchResult.getAttributeValue("cn");
@@ -1008,6 +833,134 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
 
         return new Group(groupCN, owner);
     }
+    
+    protected Group reactiveGroup(final Group newGroup, 
+                                  final Group inactiveGroup)
+        throws UserNotFoundException, LDAPException, TransientException
+    {
+        // Check requestor owns the group.
+        DN ownerDN = userPersist.getUserDN(newGroup.getOwner());
+        if (!ownerDN.equals(getSubjectDN()))
+        {
+           throw new AccessControlException(
+               "Unable to activate group " + newGroup.getID() + 
+               " because " + getSubjectDN().toString()
+               + " is not the owner"); 
+        }
+
+        List<Modification> mods = new ArrayList<Modification>();
+        mods.add(new Modification(ModificationType.DELETE, "nsaccountlock"));
+
+        Group modifiedGroup = modifyGroup(newGroup, inactiveGroup, mods);
+        Group activatedGroup = 
+                new ActivatedGroup(modifiedGroup.getID(),
+                                   modifiedGroup.getOwner());
+        activatedGroup.description = modifiedGroup.description;
+        activatedGroup.getProperties()
+                .addAll(modifiedGroup.getProperties());
+        activatedGroup.getGroupMembers()
+                .addAll(modifiedGroup.getGroupMembers());
+        activatedGroup.getUserMembers()
+                .addAll(modifiedGroup.getUserMembers());
+        return activatedGroup;
+    }
+    
+    /**
+     * 
+     * @param group
+     * @param ownerAttribute
+     * @return
+     * @throws UserNotFoundException
+     * @throws LDAPException 
+     */
+    protected LDAPResult addGroup(final Group group, 
+                                  final Attribute ownerAttribute)
+        throws UserNotFoundException, LDAPException
+    {
+        // add new group
+        List<Attribute> attributes = new ArrayList<Attribute>();
+        attributes.add(ownerAttribute);
+        attributes.add(new Attribute("objectClass", "groupofuniquenames"));
+        attributes.add(new Attribute("cn", group.getID()));
+        attributes.add(new Attribute("memberRead", 
+                                     String.valueOf(group.memberRead)));
+        
+        if (group.description != null)
+        {
+            attributes.add(new Attribute("description", group.description));
+        }
+
+        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())
+        {
+            DN grDN = getGroupDN(gr.getID());
+            members.add(grDN.toNormalizedString());
+        }
+        if (!members.isEmpty())
+        {
+            attributes.add(new Attribute("uniquemember", 
+                (String[]) members.toArray(new String[members.size()])));
+        }
+
+        AddRequest addRequest = 
+                new AddRequest(getGroupDN(group.getID()), attributes);
+
+        addRequest.addControl(
+                new ProxiedAuthorizationV2RequestControl(
+                        "dn:" + getSubjectDN().toNormalizedString()));
+
+        return getConnection().add(addRequest);
+    }
+    
+    /**
+     * 
+     * @param group
+     * @param ownerAttribute
+     * @return
+     * @throws UserNotFoundException
+     * @throws LDAPException 
+     */
+    protected LDAPResult addAdminGroup(final Group group, 
+                                       final Attribute ownerAttribute)
+        throws UserNotFoundException, LDAPException
+    {
+        // add new group
+        List<Attribute> attributes = new ArrayList<Attribute>();
+        attributes.add(ownerAttribute);
+        attributes.add(new Attribute("objectClass", "groupofuniquenames"));
+        attributes.add(new Attribute("cn", group.getID()));
+
+        List<String> admins = new ArrayList<String>();
+        for (User<?> user : group.getUserAdmins())
+        {
+            DN userDN = this.userPersist.getUserDN(user);
+            admins.add(userDN.toNormalizedString());
+        }
+        for (Group gr : group.getGroupAdmins())
+        {
+            DN grDN = getGroupDN(gr.getID());
+            admins.add(grDN.toNormalizedString());
+        }
+        if (!admins.isEmpty())
+        {
+            attributes.add(new Attribute("uniquemember", 
+                (String[]) admins.toArray(new String[admins.size()])));
+        }
+
+        AddRequest addRequest = 
+                new AddRequest(getAdminGroupDN(group.getID()), attributes);
+
+        addRequest.addControl(
+                new ProxiedAuthorizationV2RequestControl(
+                        "dn:" + getSubjectDN().toNormalizedString()));
+
+        return getConnection().add(addRequest);
+    }
 
     /**
      * Returns a group based on its LDAP DN. The returned group is bare
@@ -1038,7 +991,12 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         return group;
     }
 
-    protected DN getGroupDN(String groupID)
+    /**
+     * 
+     * @param groupID
+     * @return 
+     */
+    protected DN getGroupDN(final String groupID)
     {
         try
         {
@@ -1049,5 +1007,47 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         }
         throw new IllegalArgumentException(groupID + " not a valid group ID");
     }
+    
+    /**
+     * 
+     * @param groupID
+     * @return 
+     */
+    protected DN getAdminGroupDN(final String groupID)
+    {
+        try
+        {
+            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/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..1039a5d3205faef3b04b7b9668e058f5696b1c63 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));
@@ -651,9 +632,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..2825cdc67eb244bc2d8ca669cb4e253ab7ef2c6e 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
@@ -96,8 +96,9 @@ 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";
     
@@ -114,7 +115,7 @@ public class LdapUserDAOTest
         
         testUser = new User<X500Principal>(new X500Principal(testUserDN));
     
-        config = new LdapConfig(server, port, adminDN, adminPW, userBaseDN, groupBaseDN);
+        config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN);
     }
 
     LdapUserDAO<X500Principal> getUserDAO()
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);