diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f89bb355866379d0d2ae7455250b95e63a2cfdac
--- /dev/null
+++ b/projects/cadcAccessControl-Server/build.xml
@@ -0,0 +1,139 @@
+<!--
+************************************************************************
+*******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+**************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+*
+*  (c) 2009.                            (c) 2009.
+*  Government of Canada                 Gouvernement du Canada
+*  National Research Council            Conseil national de recherches
+*  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+*  All rights reserved                  Tous droits réservés
+*
+*  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+*  expressed, implied, or               énoncée, implicite ou légale,
+*  statutory, of any kind with          de quelque nature que ce
+*  respect to the software,             soit, concernant le logiciel,
+*  including without limitation         y compris sans restriction
+*  any warranty of merchantability      toute garantie de valeur
+*  or fitness for a particular          marchande ou de pertinence
+*  purpose. NRC shall not be            pour un usage particulier.
+*  liable in any event for any          Le CNRC ne pourra en aucun cas
+*  damages, whether direct or           être tenu responsable de tout
+*  indirect, special or general,        dommage, direct ou indirect,
+*  consequential or incidental,         particulier ou général,
+*  arising from the use of the          accessoire ou fortuit, résultant
+*  software.  Neither the name          de l'utilisation du logiciel. Ni
+*  of the National Research             le nom du Conseil National de
+*  Council of Canada nor the            Recherches du Canada ni les noms
+*  names of its contributors may        de ses  participants ne peuvent
+*  be used to endorse or promote        être utilisés pour approuver ou
+*  products derived from this           promouvoir les produits dérivés
+*  software without specific prior      de ce logiciel sans autorisation
+*  written permission.                  préalable et particulière
+*                                       par écrit.
+*
+*  This file is part of the             Ce fichier fait partie du projet
+*  OpenCADC project.                    OpenCADC.
+*
+*  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+*  you can redistribute it and/or       vous pouvez le redistribuer ou le
+*  modify it under the terms of         modifier suivant les termes de
+*  the GNU Affero General Public        la “GNU Affero General Public
+*  License as published by the          License” telle que publiée
+*  Free Software Foundation,            par la Free Software Foundation
+*  either version 3 of the              : soit la version 3 de cette
+*  License, or (at your option)         licence, soit (à votre gré)
+*  any later version.                   toute version ultérieure.
+*
+*  OpenCADC is distributed in the       OpenCADC est distribué
+*  hope that it will be useful,         dans l’espoir qu’il vous
+*  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+*  without even the implied             GARANTIE : sans même la garantie
+*  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+*  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+*  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+*  General Public License for           Générale Publique GNU Affero
+*  more details.                        pour plus de détails.
+*
+*  You should have received             Vous devriez avoir reçu une
+*  a copy of the GNU Affero             copie de la Licence Générale
+*  General Public License along         Publique GNU Affero avec
+*  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+*  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+*                                       <http://www.gnu.org/licenses/>.
+*
+*  $Revision: 4 $
+*
+************************************************************************
+-->
+
+
+<!DOCTYPE project>
+<project default="build" basedir=".">
+    <property environment="env"/>
+    <property file="local.build.properties" />
+
+    <!-- site-specific build properties or overrides of values in opencadc.properties -->
+    <property file="${env.CADC_PREFIX}/etc/local.properties" />
+
+    <!-- site-specific targets, e.g. install, cannot duplicate those in opencadc.targets.xml -->
+    <import file="${env.CADC_PREFIX}/etc/local.targets.xml" optional="true" />
+
+    <!-- default properties and targets -->
+    <property file="${env.CADC_PREFIX}/etc/opencadc.properties" />
+    <import file="${env.CADC_PREFIX}/etc/opencadc.targets.xml"/>
+
+    <!-- developer convenience: place for extra targets and properties -->
+    <import file="extras.xml" optional="true" />
+
+    <property name="project" value="cadcAccessControl-Server" />
+
+    <property name="accessControl"  value="${lib}/cadcAccessControl.jar" />
+    <property name="cadcLog"        value="${lib}/cadcLog.jar" />
+    <property name="cadcUtil"       value="${lib}/cadcUtil.jar" />
+    <property name="cadcUWS"        value="${lib}/cadcUWS.jar" />
+    
+    <property name="jdom2"          value="${ext.lib}/jdom2.jar" />
+    <property name="log4j"          value="${ext.lib}/log4j.jar" />
+    <property name="servlet"        value="${ext.lib}/servlet-api.jar" />
+    <property name="unboundid"      value="${ext.lib}/unboundid-ldapsdk-se.jar" />
+
+
+    <property name="jars" value="${accessControl}:${cadcLog}:${cadcUtil}:${cadcUWS}:${jdom2}:${log4j}:${servlet}:${unboundid}" />
+
+    <target name="build" depends="compile">
+        <jar jarfile="${build}/lib/${project}.jar"
+                    basedir="${build}/class"
+                    update="no">
+                <include name="ca/nrc/cadc/**" />
+        </jar>
+    </target>
+
+    <!-- JAR files needed to run the test suite -->
+    <property name="xerces"     value="${ext.dev}/xerces.jar" />
+    <property name="asm"        value="${ext.dev}/asm.jar" />
+    <property name="cglib"      value="${ext.dev}/cglib.jar" />
+    <property name="easymock"   value="${ext.dev}/easymock.jar" />
+    <property name="junit"      value="${ext.dev}/junit.jar" />
+    <property name="objenesis"  value="${ext.dev}/objenesis.jar" />
+    
+    <property name="testingJars" value="${build}/class:${jars}:${xerces}:${asm}:${cglib}:${easymock}:${junit}:${objenesis}" />
+
+    <target name="test" depends="compile-test">
+        <echo message="Running test" />
+
+        <!-- Run the junit test suite -->
+        <echo message="Running test suite..." />
+        <junit printsummary="yes" haltonfailure="yes" fork="yes">
+            <classpath>
+                <pathelement path="${build}/class"/>
+                <pathelement path="${build}/test/class"/>
+                <pathelement path="${testingJars}"/>
+            </classpath>
+            <test name="ca.nrc.cadc.ac.UserTest" />
+            <test name="ca.nrc.cadc.ac.GroupTest" />
+            <formatter type="plain" usefile="false" />
+        </junit>
+    </target>
+
+</project>
diff --git a/projects/cadcAccessControl-Server/config/LdapConfig.properties b/projects/cadcAccessControl-Server/config/LdapConfig.properties
new file mode 100644
index 0000000000000000000000000000000000000000..af73b275d60d77e64b1482aaa4a89f0f6450409d
--- /dev/null
+++ b/projects/cadcAccessControl-Server/config/LdapConfig.properties
@@ -0,0 +1,9 @@
+# This are the configuration fields required by the Ldap ldap-dao unit tests
+server = mach275.cadc.dao.nrc.ca
+port = 389
+admin = uid=webproxy,ou=administrators,ou=topologymanagement,o=netscaperoot
+passwd = go4it
+usersDn = ou=Users,ou=ds,dc=canfar,dc=net
+groupsDn = ou=Groups,ou=ds,dc=canfar,dc=net
+deletedGroupsDN = ou=DeletedGroups,ou=ds,dc=canfar,dc=net
+testGroupsDN = ou=TestGroups,ou=ds,dc=canfar,dc=net
\ No newline at end of file
diff --git a/projects/cadcAccessControl-Server/config/PluginFactory.properties b/projects/cadcAccessControl-Server/config/PluginFactory.properties
new file mode 100644
index 0000000000000000000000000000000000000000..43f275b133dec3ed0f9379c267447521a973dd15
--- /dev/null
+++ b/projects/cadcAccessControl-Server/config/PluginFactory.properties
@@ -0,0 +1,10 @@
+
+## commented out values are the defaults, shown as examples
+## to customise behaviour, subclass the specified class and
+## change the configuration here
+
+## UserPersistence implementation
+ca.nrc.cadc.ac.server.UserPersistence = ca.nrc.cadc.ac.server.ldap.LdapUserPersistence
+
+## GroupPersistence implementation
+ca.nrc.cadc.ac.server.GroupPersistence = ca.nrc.cadc.ac.server.ldap.LdapGroupPersistence
\ No newline at end of file
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java
new file mode 100755
index 0000000000000000000000000000000000000000..bd2bd76bf6e65014e65c7b7df44a8217f7b7cfd5
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java
@@ -0,0 +1,102 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.net.TransientException;
+import java.security.AccessControlException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Map;
+
+public abstract interface GroupPersistence<T extends Principal>
+{
+    public abstract Group getGroup(String paramString)
+        throws GroupNotFoundException, TransientException, AccessControlException;
+
+    public abstract Group addGroup(Group paramGroup)
+        throws GroupAlreadyExistsException, TransientException, AccessControlException, UserNotFoundException;
+
+    public abstract void deleteGroup(String paramString)
+        throws GroupNotFoundException, TransientException, AccessControlException;
+
+    public abstract Group modifyGroup(Group paramGroup)
+        throws GroupNotFoundException, TransientException, AccessControlException, UserNotFoundException;
+
+    public abstract Collection<Group> getGroups(Map<String, String> paramMap)
+        throws TransientException, AccessControlException;
+
+    public abstract boolean isMember(User<T> paramUser, String paramString)
+        throws TransientException, AccessControlException;
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/PluginFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/PluginFactory.java
new file mode 100755
index 0000000000000000000000000000000000000000..878a021b03cb095515898cca99c12a22b2966dbc
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/PluginFactory.java
@@ -0,0 +1,163 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server;
+
+import ca.nrc.cadc.ac.server.ldap.LdapGroupPersistence;
+import ca.nrc.cadc.ac.server.ldap.LdapUserPersistence;
+import java.net.URL;
+import java.security.Principal;
+import java.util.Properties;
+import java.util.Set;
+import org.apache.log4j.Logger;
+
+public class PluginFactory
+{
+    private static final Logger log = Logger.getLogger(PluginFactory.class);
+
+    private static final String CONFIG = PluginFactory.class.getSimpleName() + ".properties";
+    private Properties config;
+
+    public PluginFactory()
+    {
+        init();
+    }
+
+    @Override
+    public String toString()
+    {
+        return getClass().getName() + "[" + this.config.entrySet().size() + "]";
+    }
+
+    private void init()
+    {
+        this.config = new Properties();
+        URL url = null;
+        try
+        {
+            url = PluginFactory.class.getClassLoader().getResource(CONFIG);
+            if (url != null)
+            {
+                this.config.load(url.openStream());
+            }
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException("failed to read " + CONFIG + " from " + url, ex);
+        }
+    }
+
+    public <T extends Principal> GroupPersistence<T> getGroupPersistence()
+    {
+        GroupPersistence ret = null;
+        String name = GroupPersistence.class.getName();
+        String cname = this.config.getProperty(name);
+        if (cname == null)
+        {
+            ret = new LdapGroupPersistence();
+        }
+        else
+        {
+            try
+            {
+                Class c = Class.forName(cname);
+                ret = (GroupPersistence) c.newInstance();
+            }
+            catch (Exception ex)
+            {
+                throw new RuntimeException("config error: failed to create GroupPersistence " + cname, ex);
+            }
+        }
+        return ret;
+    }
+
+    public <T extends Principal> UserPersistence<T> getUserPersistence()
+    {
+        UserPersistence ret = null;
+        String name = UserPersistence.class.getName();
+        String cname = this.config.getProperty(name);
+        if (cname == null)
+        {
+            ret = new LdapUserPersistence();
+        }
+        else
+        {
+            try
+            {
+                Class c = Class.forName(cname);
+                ret = (UserPersistence) c.newInstance();
+            }
+            catch (Exception ex)
+            {
+                throw new RuntimeException("config error: failed to create UserPersistence " + cname, ex);
+            }
+        }
+        return ret;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
new file mode 100755
index 0000000000000000000000000000000000000000..31f1419b6fcc53f0c3cad2f26b3e5d1befd53d04
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
@@ -0,0 +1,82 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server;
+
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.net.TransientException;
+import java.security.AccessControlException;
+import java.security.Principal;
+
+public abstract interface UserPersistence<T extends Principal>
+{
+    public abstract User<T> getUser(T paramT)
+        throws UserNotFoundException, TransientException, AccessControlException;
+
+}
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
new file mode 100755
index 0000000000000000000000000000000000000000..04fb60ecabf904e27c1f6e28fb2adb202f0898e0
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java
@@ -0,0 +1,240 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.ldap;
+
+import ca.nrc.cadc.util.StringUtil;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+import org.apache.log4j.Logger;
+
+public class LdapConfig
+{
+    private static final Logger logger = Logger.getLogger(LdapConfig.class);
+
+    public static final String CONFIG = LdapConfig.class.getSimpleName() + ".properties";
+    public static final String LDAP_SERVER = "server";
+    public static final String LDAP_PORT = "port";
+    public static final String LDAP_ADMIN = "admin";
+    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_DELETED_GROUPS_DN = "deletedGroupsDn";
+    private String usersDN;
+    private String groupsDN;
+    private String deletedGroupsDN;
+    private String server;
+    private int port;
+    private String adminUserDN;
+    private String adminPasswd;
+
+    public static LdapConfig getLdapConfig()
+    {
+        Properties config = new Properties();
+        URL url = null;
+        try
+        {
+            url = LdapConfig.class.getClassLoader().getResource(CONFIG);
+            logger.debug("Using config from: " + url);
+            if (url != null)
+            {
+                config.load(url.openStream());
+            }
+            else
+            {
+                throw new IOException("File not found");
+            }
+
+        }
+        catch (Exception ex)
+        {
+            throw new RuntimeException("failed to read " + CONFIG + " from " + url, ex);
+        }
+
+        String server = config.getProperty("server");
+        if (!StringUtil.hasText(server))
+        {
+            throw new RuntimeException("failed to read server property");
+        }
+
+        String port = config.getProperty("port");
+        if (!StringUtil.hasText(port))
+        {
+            throw new RuntimeException("failed to read port property");
+        }
+
+        String ldapAdmin = config.getProperty("admin");
+        if (!StringUtil.hasText(ldapAdmin))
+        {
+            throw new RuntimeException("failed to read admin property");
+        }
+
+        String ldapPasswd = config.getProperty("passwd");
+        if (!StringUtil.hasText(ldapPasswd))
+        {
+            throw new RuntimeException("failed to read passwd property");
+        }
+
+        String ldapUsersDn = config.getProperty("usersDn");
+        if (!StringUtil.hasText(ldapUsersDn))
+        {
+            throw new RuntimeException("failed to read usersDn property");
+        }
+
+        String ldapGroupsDn = config.getProperty("groupsDn");
+        if (!StringUtil.hasText(ldapGroupsDn))
+        {
+            throw new RuntimeException("failed to read groupsDn property");
+        }
+
+        String ldapDeletedGroupsDn = config.getProperty("deletedGroupsDn");
+        if (!StringUtil.hasText(ldapDeletedGroupsDn))
+        {
+            throw new RuntimeException("failed to read deletedGroupsDn property");
+        }
+
+        return new LdapConfig(server, Integer.valueOf(port), ldapAdmin, ldapPasswd, ldapUsersDn, ldapGroupsDn, ldapDeletedGroupsDn);
+    }
+
+    public LdapConfig(String server, int port, String adminUserDN, String adminPasswd, String usersDN, String groupsDN, String deletedGroupsDN)
+    {
+        if (!StringUtil.hasText(server))
+        {
+            throw new IllegalArgumentException("Illegal LDAP server name: " + server);
+        }
+        if (port < 0)
+        {
+            throw new IllegalArgumentException("Illegal LDAP server port: " + port);
+        }
+        if (!StringUtil.hasText(adminUserDN))
+        {
+            throw new IllegalArgumentException("Illegal Admin DN: " + adminUserDN);
+        }
+        if (!StringUtil.hasText(adminPasswd))
+        {
+            throw new IllegalArgumentException("Illegal Admin password: " + adminPasswd);
+        }
+        if (!StringUtil.hasText(usersDN))
+        {
+            throw new IllegalArgumentException("Illegal users LDAP DN: " + usersDN);
+        }
+        if (!StringUtil.hasText(groupsDN))
+        {
+            throw new IllegalArgumentException("Illegal groups LDAP DN: " + groupsDN);
+        }
+        if (!StringUtil.hasText(deletedGroupsDN))
+        {
+            throw new IllegalArgumentException("Illegal groups LDAP DN: " + deletedGroupsDN);
+        }
+
+        this.server = server;
+        this.port = port;
+        this.adminUserDN = adminUserDN;
+        this.adminPasswd = adminPasswd;
+        this.usersDN = usersDN;
+        this.groupsDN = groupsDN;
+        this.deletedGroupsDN = deletedGroupsDN;
+    }
+
+    public String getUsersDN()
+    {
+        return this.usersDN;
+    }
+
+    public String getGroupsDN()
+    {
+        return this.groupsDN;
+    }
+
+    public String getDeletedGroupsDN()
+    {
+        return this.deletedGroupsDN;
+    }
+
+    public String getServer()
+    {
+        return this.server;
+    }
+
+    public int getPort()
+    {
+        return this.port;
+    }
+
+    public String getAdminUserDN()
+    {
+        return this.adminUserDN;
+    }
+
+    public String getAdminPasswd()
+    {
+        return this.adminPasswd;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java
new file mode 100755
index 0000000000000000000000000000000000000000..0b16851ecf283232bd1422d19b68a4789cb898c4
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java
@@ -0,0 +1,184 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.ldap;
+
+import ca.nrc.cadc.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.NumericPrincipal;
+import ca.nrc.cadc.auth.OpenIdPrincipal;
+import com.unboundid.ldap.sdk.DN;
+import com.unboundid.ldap.sdk.LDAPConnection;
+import com.unboundid.ldap.sdk.LDAPException;
+import com.unboundid.ldap.sdk.SearchResult;
+import com.unboundid.ldap.sdk.SearchResultEntry;
+import com.unboundid.ldap.sdk.SearchScope;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.List;
+import java.util.Set;
+import javax.security.auth.Subject;
+import javax.security.auth.x500.X500Principal;
+
+public abstract class LdapDAO
+{
+    private LDAPConnection conn;
+    LdapConfig config;
+    DN subjDN = null;
+
+    public LdapDAO(LdapConfig config)
+    {
+        if (config == null)
+        {
+            throw new IllegalArgumentException("LDAP config required");
+        }
+        this.config = config;
+    }
+
+    public void close()
+    {
+        if (this.conn != null)
+        {
+            this.conn.close();
+        }
+    }
+
+    protected LDAPConnection getConnection() throws LDAPException, AccessControlException
+    {
+        if (this.conn == null)
+        {
+            this.conn = new LDAPConnection(this.config.getServer(), this.config.getPort());
+            this.conn.bind(this.config.getAdminUserDN(), this.config.getAdminPasswd());
+
+            Subject callerSubject = Subject.getSubject(AccessController.getContext());
+
+            if (callerSubject == null)
+            {
+                throw new AccessControlException("Caller not authenticated.");
+            }
+            Set<Principal> principals = callerSubject.getPrincipals();
+            if (principals.size() < 1)
+            {
+                throw new AccessControlException("Caller not authenticated.");
+            }
+            String ldapField = null;
+            for (Principal p : principals)
+            {
+                if ((p instanceof HttpPrincipal))
+                {
+                    ldapField = "(uid=" + p.getName() + ")";
+                    break;
+                }
+                if ((p instanceof NumericPrincipal))
+                {
+                    ldapField = "(entryid=" + p.getName() + ")";
+                    break;
+                }
+                if ((p instanceof X500Principal))
+                {
+                    ldapField = "(distinguishedname=" + p.getName() + ")";
+                    break;
+                }
+                if ((p instanceof OpenIdPrincipal))
+                {
+                    ldapField = "(openid=" + p.getName() + ")";
+                    break;
+                }
+            }
+
+            if (ldapField == null)
+            {
+                throw new AccessControlException("Identity of caller unknown.");
+            }
+
+            SearchResult searchResult = this.conn.search(this.config.getUsersDN(), SearchScope.ONE, ldapField, new String[]
+                                                     {
+                                                         "entrydn"
+            });
+
+            if (searchResult.getEntryCount() < 1)
+            {
+                throw new AccessControlException("No LDAP account when search with rule " + ldapField);
+            }
+
+            this.subjDN = ((SearchResultEntry) searchResult.getSearchEntries().get(0)).getAttributeValueAsDN("entrydn");
+        }
+
+        return this.conn;
+    }
+
+    protected DN getSubjectDN() throws LDAPException
+    {
+        if (this.subjDN == null)
+        {
+            getConnection();
+        }
+        return this.subjDN;
+    }
+
+}
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
new file mode 100755
index 0000000000000000000000000000000000000000..4c0df74c0bf2ba72459fad57727a09395a0ffa63
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
@@ -0,0 +1,543 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.ldap;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.net.TransientException;
+import com.unboundid.ldap.sdk.AddRequest;
+import com.unboundid.ldap.sdk.Attribute;
+import com.unboundid.ldap.sdk.DN;
+import com.unboundid.ldap.sdk.LDAPConnection;
+import com.unboundid.ldap.sdk.LDAPException;
+import com.unboundid.ldap.sdk.LDAPResult;
+import com.unboundid.ldap.sdk.Modification;
+import com.unboundid.ldap.sdk.ModificationType;
+import com.unboundid.ldap.sdk.ModifyDNRequest;
+import com.unboundid.ldap.sdk.ModifyRequest;
+import com.unboundid.ldap.sdk.SearchRequest;
+import com.unboundid.ldap.sdk.SearchResultEntry;
+import com.unboundid.ldap.sdk.SearchScope;
+import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl;
+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.Map;
+import java.util.Set;
+import org.apache.log4j.Logger;
+
+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 static final String PUB_GROUP_ACI = "(targetattr = \"*\") (version 3.0;acl \"Group Public\";allow (read,compare,search)userdn=\"ldap:///anyone\";)";
+    private LdapUserDAO<T> userPersist;
+
+    public LdapGroupDAO(LdapConfig config, LdapUserDAO<T> userPersist)
+    {
+        super(config);
+        if (userPersist == null)
+        {
+            throw new IllegalArgumentException("User persistence instance required");
+        }
+
+        this.userPersist = userPersist;
+    }
+
+    public Group getGroup(String groupID)
+        throws GroupNotFoundException, TransientException, AccessControlException
+    {
+        return getGroup(groupID, true);
+    }
+
+    public Group addGroup(Group group)
+        throws GroupAlreadyExistsException, TransientException, UserNotFoundException
+    {
+        try
+        {
+            getGroup(group.getID());
+            throw new GroupAlreadyExistsException(group.getID());
+        }
+        catch (GroupNotFoundException ex)
+        {
+            try
+            {
+                if (group.getProperties().size() > 0)
+                {
+                    throw new UnsupportedOperationException("Support for groups properties not available");
+                }
+
+                DN ownerDN = this.userPersist.getUserDN(group.getOwner());
+                String groupWriteAci = null;
+                String groupReadAci = null;
+                if (group.groupWrite != null)
+                {
+                    DN groupWrite = getGroupDN(group.groupWrite.getID());
+                    groupWriteAci = "(targetattr = \"*\") (version 3.0;acl \"Group Write\";allow (read,compare,search,selfwrite,write,add)(groupdn = \"ldap:///<ACTUAL_GROUP>\");)".replace("<ACTUAL_GROUP>", groupWrite.toNormalizedString());
+                }
+
+                if (group.groupRead != null)
+                {
+                    DN groupRead = getGroupDN(group.groupRead.getID());
+                    groupReadAci = "(targetattr = \"*\") (version 3.0;acl \"Group Read\";allow (read,compare,search)(groupdn = \"ldap:///<ACTUAL_GROUP>\");)".replace("<ACTUAL_GROUP>", groupRead.toNormalizedString());
+                }
+
+                List attributes = new ArrayList();
+                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()));
+
+                List acis = new ArrayList();
+                if (group.publicRead)
+                {
+                    acis.add("(targetattr = \"*\") (version 3.0;acl \"Group Public\";allow (read,compare,search)userdn=\"ldap:///anyone\";)");
+                }
+                if (groupWriteAci != null)
+                {
+                    acis.add(groupWriteAci);
+                }
+                if (groupReadAci != null)
+                {
+                    acis.add(groupReadAci);
+                }
+
+                if (acis.size() > 0)
+                {
+                    attributes.add(new Attribute("aci", (String[]) acis.toArray(new String[acis.size()])));
+                }
+
+                List members = new ArrayList();
+                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.size() > 0)
+                {
+                    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()));
+
+                LDAPResult result = getConnection().add(addRequest);
+                try
+                {
+                    return getGroup(group.getID());
+                }
+                catch (GroupNotFoundException e)
+                {
+                    throw new RuntimeException("BUG: new group not found");
+                }
+            }
+            catch (LDAPException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public void deleteGroup(String groupID)
+        throws GroupNotFoundException, TransientException
+    {
+        Group group = getGroup(groupID, false);
+
+        DN groupDN = getGroupDN(group.getID());
+        try
+        {
+            ModifyDNRequest modifyDNRequest = new ModifyDNRequest(group.getID(), group.getID(), true, this.config.getDeletedGroupsDN());
+
+            modifyDNRequest.addControl(new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString()));
+
+            LDAPResult result = getConnection().modifyDN(modifyDNRequest);
+        }
+        catch (LDAPException e1)
+        {
+            throw new RuntimeException("LDAP problem", e1);
+        }
+        try
+        {
+            getGroup(group.getID());
+            throw new RuntimeException("BUG: group not deleted " + group.getID());
+        }
+        catch (GroupNotFoundException ignore)
+        {
+        }
+    }
+
+    public Collection<Group> getGroups(Map<String, String> criteria)
+        throws TransientException, AccessControlException
+    {
+        return null;
+    }
+
+    private Group getGroup(String groupID, boolean withMembers)
+        throws GroupNotFoundException, TransientException, AccessControlException
+    {
+        try
+        {
+            SearchRequest searchRequest = 
+                new SearchRequest(
+                    this.config.getGroupsDN(), SearchScope.SUB, "(cn=" + groupID + ")", 
+                    new String[] {"entrydn", "entryid", "cn", "description", "owner", "uniquemember", "aci", "modifytimestamp" });
+
+            searchRequest.addControl(new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString()));
+
+            SearchResultEntry group = getConnection().searchForEntry(searchRequest);
+
+            if (group == null)
+            {
+                String msg = "Group not found " + groupID;
+                logger.debug(msg);
+                throw new GroupNotFoundException(groupID);
+            }
+            String groupCN = group.getAttributeValue("cn");
+            DN groupOwner = group.getAttributeValueAsDN("owner");
+            Long grID = group.getAttributeValueAsLong("entryid");
+            Date lastModified = group.getAttributeValueAsDate("modifytimestamp");
+            User owner;
+            try
+            {
+                owner = this.userPersist.getMember(groupOwner);
+            }
+            catch (UserNotFoundException e)
+            {
+                throw new RuntimeException("BUG: group owner not found");
+            }
+            Group ldapGroup = new Group(groupCN, owner);
+
+            ldapGroup.description = group.getAttributeValue("description");
+
+            ldapGroup.lastModified = lastModified;
+
+            if (withMembers)
+            {
+                if (group.getAttributeValues("uniquemember") != null)
+                {
+                    for (String member : group.getAttributeValues("uniquemember"))
+                    {
+                        DN memberDN = new DN(member);
+                        if (memberDN.isDescendantOf(this.config.getUsersDN(), false))
+                        {
+                            User usr;
+                            try
+                            {
+                                usr = this.userPersist.getMember(memberDN);
+                            }
+                            catch (UserNotFoundException e)
+                            {
+                                throw new RuntimeException("BUG: group member not found");
+                            }
+
+                            ldapGroup.getUserMembers().add(usr);
+                        }
+                        else if (memberDN.isDescendantOf(this.config.getGroupsDN(), false))
+                        {
+                            Group memberGroup = getGroup(memberDN);
+                            ldapGroup.getGroupMembers().add(memberGroup);
+                        }
+                        else
+                        {
+                            throw new RuntimeException("BUG: unknown member DN type: " + memberDN);
+                        }
+
+                    }
+
+                }
+
+                if (group.getAttributeValues("aci") != null)
+                {
+                    for (String aci : group.getAttributeValues("aci"))
+                    {
+                        if (aci.contains("Group Read"))
+                        {
+                            String grRead = aci.substring(aci.indexOf("ldap:///"));
+
+                            grRead = grRead.substring(grRead.indexOf("cn"), grRead.lastIndexOf('"'));
+
+                            Group groupRead = getGroup(new DN(grRead));
+                            ldapGroup.groupRead = groupRead;
+                        }
+                        else if (aci.contains("Group Write"))
+                        {
+                            String grWrite = aci.substring(aci.indexOf("ldap:///"));
+
+                            grWrite = grWrite.substring(grWrite.indexOf("cn"), grWrite.lastIndexOf('"'));
+
+                            Group groupWrite = getGroup(new DN(grWrite));
+
+                            ldapGroup.groupWrite = groupWrite;
+                        }
+                        else if (aci.equals("(targetattr = \"*\") (version 3.0;acl \"Group Public\";allow (read,compare,search)userdn=\"ldap:///anyone\";)"))
+                        {
+                            ldapGroup.publicRead = true;
+                        }
+                    }
+                }
+            }
+
+            return ldapGroup;
+        }
+        catch (LDAPException e1)
+        {
+            throw new TransientException("Error getting the group", e1);
+        }
+        catch (UserNotFoundException e2)
+        {
+            throw new RuntimeException("BUG - owner or member not found", e2);
+        }
+    }
+
+    public Group modifyGroup(Group group)
+        throws GroupNotFoundException, TransientException, AccessControlException, UserNotFoundException
+    {
+        Group oldGroup = getGroup(group.getID());
+        if (group.getProperties().size() > 0)
+        {
+            throw new UnsupportedOperationException("Support for groups properties not available");
+        }
+
+        List modifs = new ArrayList();
+        if (group.description == null)
+        {
+            modifs.add(new Modification(ModificationType.DELETE, "description"));
+        }
+        else
+        {
+            modifs.add(new Modification(ModificationType.REPLACE, "description", group.description));
+        }
+
+        List acis = new ArrayList();
+        if (group.groupRead != null)
+        {
+            if (group.groupRead.equals(group))
+            {
+                throw new IllegalArgumentException("cyclical reference from groupRead to group");
+            }
+
+            DN readGrDN = getGroupDN(group.groupRead.getID());
+            acis.add("(targetattr = \"*\") (version 3.0;acl \"Group Read\";allow (read,compare,search)(groupdn = \"ldap:///<ACTUAL_GROUP>\");)".replace("<ACTUAL_GROUP>", readGrDN.toNormalizedString()));
+        }
+
+        if (group.groupWrite != null)
+        {
+            if (group.groupWrite.equals(group))
+            {
+                throw new IllegalArgumentException("cyclical reference from groupWrite to group");
+            }
+
+            DN writeGrDN = getGroupDN(group.groupWrite.getID());
+            acis.add("(targetattr = \"*\") (version 3.0;acl \"Group Write\";allow (read,compare,search,selfwrite,write,add)(groupdn = \"ldap:///<ACTUAL_GROUP>\");)".replace("<ACTUAL_GROUP>", writeGrDN.toNormalizedString()));
+        }
+
+        if (group.publicRead)
+        {
+            acis.add("(targetattr = \"*\") (version 3.0;acl \"Group Public\";allow (read,compare,search)userdn=\"ldap:///anyone\";)");
+        }
+        modifs.add(new Modification(ModificationType.REPLACE, "aci", (String[]) acis.toArray(new String[acis.size()])));
+
+        List newMembers = new ArrayList();
+        for (User member : group.getUserMembers())
+        {
+            if (!oldGroup.getUserMembers().remove(member))
+            {
+                DN memberDN;
+                try
+                {
+                    memberDN = this.userPersist.getUserDN(member);
+                }
+                catch (LDAPException e)
+                {
+                    throw new UserNotFoundException("User not found " + member.getUserID());
+                }
+
+                newMembers.add(memberDN.toNormalizedString());
+            }
+        }
+        for (Group gr : group.getGroupMembers())
+        {
+            if (gr.equals(group))
+            {
+                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.size() > 0)
+        {
+            modifs.add(new Modification(ModificationType.ADD, "uniquemember", (String[]) newMembers.toArray(new String[newMembers.size()])));
+        }
+
+        List delMembers = new ArrayList();
+        for (User member : oldGroup.getUserMembers())
+        {
+            DN memberDN;
+            try
+            {
+                memberDN = this.userPersist.getUserDN(member);
+            }
+            catch (LDAPException e)
+            {
+                throw new UserNotFoundException("User not found " + member.getUserID());
+            }
+
+            delMembers.add(memberDN.toNormalizedString());
+        }
+        for (Group gr : oldGroup.getGroupMembers())
+        {
+            DN grDN = getGroupDN(gr.getID());
+            delMembers.add(grDN.toNormalizedString());
+        }
+        if (delMembers.size() > 0)
+        {
+            modifs.add(new Modification(ModificationType.DELETE, "uniquemember", (String[]) delMembers.toArray(new String[delMembers.size()])));
+        }
+
+        ModifyRequest modifyRequest = new ModifyRequest(getGroupDN(group.getID()), modifs);
+        try
+        {
+            modifyRequest.addControl(new ProxiedAuthorizationV2RequestControl("dn:" + getSubjectDN().toNormalizedString()));
+            LDAPResult result = getConnection().modify(modifyRequest);
+        }
+        catch (LDAPException e1)
+        {
+            throw new RuntimeException("LDAP problem", e1);
+        }
+        try
+        {
+            return getGroup(group.getID());
+        }
+        catch (GroupNotFoundException e)
+        {
+        }
+        throw new RuntimeException("BUG: new group not found");
+    }
+
+    protected Group getGroup(DN groupDN)
+        throws LDAPException, GroupNotFoundException, UserNotFoundException
+    {
+        SearchResultEntry searchResult = null;
+
+        searchResult = getConnection().getEntry(groupDN.toNormalizedString(), new String[]
+                                            {
+                                                "cn", "description", "owner"
+        });
+
+        if (searchResult == null)
+        {
+            String msg = "Group not found " + groupDN;
+            logger.debug(msg);
+            throw new GroupNotFoundException(groupDN.toNormalizedString());
+        }
+
+        DN ownerDN = searchResult.getAttributeValueAsDN("owner");
+        User owner = this.userPersist.getMember(ownerDN);
+        Group group = new Group(searchResult.getAttributeValue("cn"), owner);
+
+        return group;
+    }
+
+    protected DN getGroupDN(String groupID)
+    {
+        try
+        {
+            return new DN("cn=" + groupID + "," + this.config.getGroupsDN());
+        }
+        catch (LDAPException e)
+        {
+        }
+        throw new IllegalArgumentException(groupID + " not a valid group ID");
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java
new file mode 100755
index 0000000000000000000000000000000000000000..e8463e615a54c4457f5ed2669a9d171ab784f6c0
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java
@@ -0,0 +1,182 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.ldap;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import ca.nrc.cadc.net.TransientException;
+import java.security.AccessControlException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Map;
+import org.apache.log4j.Logger;
+
+public class LdapGroupPersistence<T extends Principal>
+    implements GroupPersistence<T>
+{
+    private static final Logger logger = Logger.getLogger(LdapGroupPersistence.class);
+    private LdapConfig config;
+
+    public LdapGroupPersistence()
+    {
+        this.config = LdapConfig.getLdapConfig();
+    }
+
+    public Group getGroup(String groupName)
+        throws GroupNotFoundException, TransientException, AccessControlException
+    {
+        LdapGroupDAO groupDAO = null;
+        try
+        {
+            groupDAO = new LdapGroupDAO(this.config, new LdapUserDAO(this.config));
+            Group ret = groupDAO.getGroup(groupName);
+            return ret;
+        }
+        finally
+        {
+            if (groupDAO != null)
+            {
+                groupDAO.close();
+            }
+        }
+    }
+
+    public Group addGroup(Group group)
+        throws GroupAlreadyExistsException, TransientException, AccessControlException, UserNotFoundException
+    {
+        LdapGroupDAO groupDAO = null;
+        try
+        {
+            groupDAO = new LdapGroupDAO(this.config, new LdapUserDAO(this.config));
+            Group ret = groupDAO.addGroup(group);
+            return ret;
+        }
+        finally
+        {
+            if (groupDAO != null)
+            {
+                groupDAO.close();
+            }
+        }
+    }
+
+    public void deleteGroup(String groupName)
+        throws GroupNotFoundException, TransientException, AccessControlException
+    {
+        LdapGroupDAO groupDAO = null;
+        try
+        {
+            groupDAO = new LdapGroupDAO(this.config, new LdapUserDAO(this.config));
+            groupDAO.deleteGroup(groupName);
+        }
+        finally
+        {
+            if (groupDAO != null)
+            {
+                groupDAO.close();
+            }
+        }
+    }
+
+    public Group modifyGroup(Group group)
+        throws GroupNotFoundException, TransientException, AccessControlException, UserNotFoundException
+    {
+        LdapGroupDAO groupDAO = null;
+        try
+        {
+            groupDAO = new LdapGroupDAO(this.config, new LdapUserDAO(this.config));
+            Group ret = groupDAO.modifyGroup(group);
+            return ret;
+        }
+        finally
+        {
+            if (groupDAO != null)
+            {
+                groupDAO.close();
+            }
+        }
+    }
+
+    public Collection<Group> getGroups(Map<String, String> criteria)
+        throws TransientException, AccessControlException
+    {
+        throw new UnsupportedOperationException("To be implemented");
+    }
+
+    public boolean isMember(User<T> member, String groupName)
+        throws TransientException, AccessControlException
+    {
+        throw new UnsupportedOperationException("To be implemented");
+    }
+
+}
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
new file mode 100755
index 0000000000000000000000000000000000000000..2fb52ecd9d1e853f2ea67955225669859a8fa993
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
@@ -0,0 +1,192 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.ldap;
+
+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.DN;
+import com.unboundid.ldap.sdk.LDAPConnection;
+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 java.security.AccessControlException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+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);
+
+    private Map<Class<?>, String> attribType = new HashMap();
+
+    public LdapUserDAO(LdapConfig config)
+    {
+        super(config);
+        this.attribType.put(HttpPrincipal.class, "cn");
+        this.attribType.put(X500Principal.class, "distinguishedname");
+        this.attribType.put(NumericPrincipal.class, "entryid");
+    }
+
+    public User<T> getUser(T userID)
+        throws UserNotFoundException, TransientException, AccessControlException
+    {
+        String searchField = (String) this.attribType.get(userID.getClass());
+        if (searchField == null)
+        {
+            throw new IllegalArgumentException("Unsupported principal type " + userID.getClass());
+        }
+
+        searchField = "(" + searchField + "=" + userID.getName() + ")";
+
+        SearchResultEntry searchResult = null;
+        try
+        {
+            SearchRequest searchRequest = new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, searchField, new String[]
+                                                        {
+                                                            "cn", "entryid", "entrydn", "dn"
+            });
+
+            searchRequest.addControl(new ProxiedAuthorizationV1RequestControl(getSubjectDN()));
+
+            searchResult = getConnection().searchForEntry(searchRequest);
+        }
+        catch (LDAPException e)
+        {
+            e.printStackTrace();
+        }
+
+        if (searchResult == null)
+        {
+            String msg = "User not found " + userID.toString();
+            logger.debug(msg);
+            throw new UserNotFoundException(msg);
+        }
+        User user = new User(userID);
+        user.getIdentities().add(new HttpPrincipal(searchResult.getAttributeValue("cn")));
+
+        user.getIdentities().add(new NumericPrincipal(searchResult.getAttributeValueAsInteger("entryid")));
+
+        return user;
+    }
+
+    User<X500Principal> getMember(DN userDN)
+        throws UserNotFoundException, LDAPException
+    {
+        SearchResultEntry searchResult = getConnection().getEntry(userDN.toNormalizedString(), (String[]) this.attribType.values().toArray(new String[this.attribType.values().size()]));
+
+        if (searchResult == null)
+        {
+            String msg = "User not found " + userDN;
+            logger.debug(msg);
+            throw new UserNotFoundException(msg);
+        }
+        User user = new User(new X500Principal(searchResult.getAttributeValue((String) this.attribType.get(X500Principal.class))));
+
+        return user;
+    }
+
+    DN getUserDN(User<? extends Principal> user)
+        throws LDAPException, UserNotFoundException
+    {
+        String searchField = (String) this.attribType.get(user.getUserID().getClass());
+        if (searchField == null)
+        {
+            throw new IllegalArgumentException("Unsupported principal type " + user.getUserID().getClass());
+        }
+
+        searchField = "(" + searchField + "=" + user.getUserID().getName() + ")";
+
+        SearchRequest searchRequest = 
+                new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, 
+                                 searchField, new String[] {"entrydn"});
+
+        searchRequest.addControl(new ProxiedAuthorizationV1RequestControl(getSubjectDN()));
+
+        SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest);
+
+        if (searchResult == null)
+        {
+            String msg = "User not found " + user.getUserID().toString();
+            logger.debug(msg);
+            throw new UserNotFoundException(msg);
+        }
+        return searchResult.getAttributeValueAsDN("entrydn");
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java
new file mode 100755
index 0000000000000000000000000000000000000000..ebdd209e9cbbece66f931a6b61da485cea096e8a
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java
@@ -0,0 +1,116 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.ldap;
+
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.net.TransientException;
+import java.security.AccessControlException;
+import java.security.Principal;
+import org.apache.log4j.Logger;
+
+public class LdapUserPersistence<T extends Principal>
+    implements UserPersistence<T>
+{
+    private static final Logger logger = Logger.getLogger(LdapUserPersistence.class);
+    private LdapConfig config;
+
+    public LdapUserPersistence()
+    {
+        try
+        {
+            this.config = LdapConfig.getLdapConfig();
+        }
+        catch (RuntimeException e)
+        {
+            logger.error("test/config/LdapConfig.properties file required.", e);
+        }
+    }
+
+    public User<T> getUser(T userID)
+        throws UserNotFoundException, TransientException, AccessControlException
+    {
+        LdapUserDAO userDAO = null;
+        try
+        {
+            userDAO = new LdapUserDAO(this.config);
+            User ret = userDAO.getUser(userID);
+            return ret;
+        }
+        finally
+        {
+            if (userDAO != null)
+            {
+                userDAO.close();
+            }
+        }
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ACSearchRunner.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ACSearchRunner.java
new file mode 100755
index 0000000000000000000000000000000000000000..56d9d77d10aaaa08dbe75adb5d7cd1d42ade6b69
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ACSearchRunner.java
@@ -0,0 +1,102 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.uws.Job;
+import ca.nrc.cadc.uws.server.JobRunner;
+import ca.nrc.cadc.uws.server.JobUpdater;
+import ca.nrc.cadc.uws.server.SyncOutput;
+
+public class ACSearchRunner
+    implements JobRunner
+{
+    private JobUpdater jobUpdater;
+    private SyncOutput syncOut;
+    private Job job;
+
+    public void setJobUpdater(JobUpdater jobUpdater)
+    {
+        this.jobUpdater = jobUpdater;
+    }
+
+    public void setJob(Job job)
+    {
+        this.job = job;
+    }
+
+    public void setSyncOutput(SyncOutput syncOut)
+    {
+        this.syncOut = syncOut;
+    }
+
+    public void run()
+    {
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/AddGroupMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/AddGroupMemberAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..8dda2df4c849bf5f76919b8c5e157adb3475775e
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/AddGroupMemberAction.java
@@ -0,0 +1,108 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class AddGroupMemberAction extends GroupsAction
+{
+    private String groupName;
+    private String groupMemberName;
+
+    AddGroupMemberAction(GroupLogInfo logInfo, String groupName, String groupMemberName)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+        this.groupMemberName = groupMemberName;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        Group group = groupPersistence.getGroup(this.groupName);
+        Group toAdd = groupPersistence.getGroup(this.groupMemberName);
+        if (!group.getGroupMembers().add(toAdd))
+        {
+            throw new GroupAlreadyExistsException(this.groupMemberName);
+        }
+        groupPersistence.modifyGroup(group);
+
+        List addedMembers = new ArrayList();
+        addedMembers.add(toAdd.getID());
+        logGroupInfo(group.getID(), null, addedMembers);
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/AddUserMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/AddUserMemberAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..22e55998e8fa25bf510a58184ce1ac28eb19b154
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/AddUserMemberAction.java
@@ -0,0 +1,116 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.MemberAlreadyExistsException;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class AddUserMemberAction extends GroupsAction
+{
+    private String groupName;
+    private String userID;
+    private String userIDType;
+
+    AddUserMemberAction(GroupLogInfo logInfo, String groupName, String userID, String userIDType)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+        this.userID = userID;
+        this.userIDType = userIDType;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        UserPersistence userPersistence = getUserPersistence();
+        Group group = groupPersistence.getGroup(this.groupName);
+        Principal userPrincipal = AuthenticationUtil.createPrincipal(this.userID, this.userIDType);
+        User toAdd = userPersistence.getUser(userPrincipal);
+        if (!group.getUserMembers().add(toAdd))
+        {
+            throw new MemberAlreadyExistsException();
+        }
+        groupPersistence.modifyGroup(group);
+
+        List addedMembers = new ArrayList();
+        addedMembers.add(toAdd.getUserID().getName());
+        logGroupInfo(group.getID(), null, addedMembers);
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..16a7477a7beb6a286e784da480ad0910ea82d722
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java
@@ -0,0 +1,119 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupReader;
+import ca.nrc.cadc.ac.GroupWriter;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import javax.servlet.http.HttpServletResponse;
+
+public class CreateGroupAction extends GroupsAction
+{
+    private InputStream inputStream;
+
+    CreateGroupAction(GroupLogInfo logInfo, InputStream inputStream)
+    {
+        super(logInfo);
+        this.inputStream = inputStream;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        Group group = GroupReader.read(this.inputStream);
+        Group newGroup = groupPersistence.addGroup(group);
+        this.response.setContentType("application/xml");
+        GroupWriter.write(newGroup, this.response.getOutputStream());
+
+        List addedMembers = null;
+        if ((newGroup.getUserMembers().size() > 0) || (newGroup.getGroupMembers().size() > 0))
+        {
+            addedMembers = new ArrayList();
+            for (Group gr : newGroup.getGroupMembers())
+            {
+                addedMembers.add(gr.getID());
+            }
+            for (User usr : newGroup.getUserMembers())
+            {
+                addedMembers.add(usr.getUserID().getName());
+            }
+        }
+        logGroupInfo(newGroup.getID(), null, addedMembers);
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/DeleteGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/DeleteGroupAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..0ea035fae7efb9aaa7470e7cfcf18e935ac25c06
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/DeleteGroupAction.java
@@ -0,0 +1,110 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class DeleteGroupAction extends GroupsAction
+{
+    private String groupName;
+
+    DeleteGroupAction(GroupLogInfo logInfo, String groupName)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        Group deletedGroup = groupPersistence.getGroup(this.groupName);
+        groupPersistence.deleteGroup(this.groupName);
+        if ((deletedGroup.getUserMembers().size() > 0) || (deletedGroup.getGroupMembers().size() > 0))
+        {
+            this.logInfo.addedMembers = new ArrayList();
+            for (Group gr : deletedGroup.getGroupMembers())
+            {
+                this.logInfo.deletedMembers.add(gr.getID());
+            }
+            for (User usr : deletedGroup.getUserMembers())
+            {
+                this.logInfo.deletedMembers.add(usr.getUserID().getName());
+            }
+        }
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..d4d50f1d097ebe7a611588bdbb4200d974efb28e
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java
@@ -0,0 +1,95 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupWriter;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import javax.servlet.http.HttpServletResponse;
+
+public class GetGroupAction extends GroupsAction
+{
+    private String groupName;
+
+    GetGroupAction(GroupLogInfo logInfo, String groupName)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        Group group = groupPersistence.getGroup(this.groupName);
+        this.response.setContentType("application/xml");
+        GroupWriter.write(group, this.response.getOutputStream());
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupLogInfo.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupLogInfo.java
new file mode 100755
index 0000000000000000000000000000000000000000..512610114aa61aa3cc24d30a2fd62c3922f93e7c
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupLogInfo.java
@@ -0,0 +1,86 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.log.ServletLogInfo;
+import java.util.List;
+import javax.servlet.http.HttpServletRequest;
+
+public class GroupLogInfo extends ServletLogInfo
+{
+    public String groupID;
+    public List<String> addedMembers;
+    public List<String> deletedMembers;
+
+    public GroupLogInfo(HttpServletRequest request)
+    {
+        super(request);
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..e1846d21afb1c07032606f2d7344dd077543e550
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java
@@ -0,0 +1,247 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.MemberAlreadyExistsException;
+import ca.nrc.cadc.ac.MemberNotFoundException;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import ca.nrc.cadc.ac.server.PluginFactory;
+import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.net.TransientException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.security.AccessControlException;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+import javax.security.auth.Subject;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.log4j.Logger;
+
+public abstract class GroupsAction
+    implements PrivilegedExceptionAction<Object>
+{
+    private static final Logger log = Logger.getLogger(GroupsAction.class);
+    protected GroupLogInfo logInfo;
+    protected HttpServletResponse response;
+
+    GroupsAction(GroupLogInfo logInfo)
+    {
+        this.logInfo = logInfo;
+    }
+
+    public void doAction(Subject subject, HttpServletResponse response)
+        throws IOException
+    {
+        try
+        {
+            try
+            {
+                this.response = response;
+
+                if (subject == null)
+                {
+                    run();
+                }
+                else
+                {
+                    Subject.doAs(subject, this);
+                }
+            }
+            catch (PrivilegedActionException e)
+            {
+                Throwable cause = e.getCause();
+                if (cause != null)
+                {
+                    throw cause;
+                }
+                throw e;
+            }
+        }
+        catch (AccessControlException e)
+        {
+            log.debug(e);
+            String message = "Unauthorized";
+            this.logInfo.setMessage(message);
+            sendError(401, message);
+        }
+        catch (IllegalArgumentException e)
+        {
+            log.debug(e);
+            String message = e.getMessage();
+            this.logInfo.setMessage(message);
+            sendError(400, message);
+        }
+        catch (MemberNotFoundException e)
+        {
+            log.debug(e);
+            String message = "Member not found: " + e.getMessage();
+            this.logInfo.setMessage(message);
+            sendError(404, message);
+        }
+        catch (GroupNotFoundException e)
+        {
+            log.debug(e);
+            String message = "Group not found: " + e.getMessage();
+            this.logInfo.setMessage(message);
+            sendError(404, message);
+        }
+        catch (UserNotFoundException e)
+        {
+            log.debug(e);
+            String message = "User not found: " + e.getMessage();
+            this.logInfo.setMessage(message);
+            sendError(404, message);
+        }
+        catch (MemberAlreadyExistsException e)
+        {
+            log.debug(e);
+            String message = "Member already exists: " + e.getMessage();
+            this.logInfo.setMessage(message);
+            sendError(409, message);
+        }
+        catch (GroupAlreadyExistsException e)
+        {
+            log.debug(e);
+            String message = "Group already exists: " + e.getMessage();
+            this.logInfo.setMessage(message);
+            sendError(409, message);
+        }
+        catch (UnsupportedOperationException e)
+        {
+            log.debug(e);
+            this.logInfo.setMessage("Not yet implemented.");
+            sendError(501);
+        }
+        catch (TransientException e)
+        {
+            String message = "Internal Transient Error: " + e.getMessage();
+            this.logInfo.setSuccess(false);
+            this.logInfo.setMessage(message);
+            log.error(message, e);
+            sendError(503, message);
+        }
+        catch (Throwable t)
+        {
+            String message = "Internal Error: " + t.getMessage();
+            this.logInfo.setSuccess(false);
+            this.logInfo.setMessage(message);
+            log.error(message, t);
+            sendError(500, message);
+        }
+    }
+
+    private void sendError(int responseCode)
+        throws IOException
+    {
+        sendError(responseCode, null);
+    }
+
+    private void sendError(int responseCode, String message)
+        throws IOException
+    {
+        if (!this.response.isCommitted())
+        {
+            this.response.setContentType("text/plain");
+            if (message != null)
+            {
+                this.response.getWriter().write(message);
+            }
+            this.response.setStatus(responseCode);
+        }
+        else
+        {
+            log.warn("Could not send error " + responseCode + " (" + message + ") because the response is already committed.");
+        }
+    }
+
+    <T extends Principal> GroupPersistence<T> getGroupPersistence()
+    {
+        PluginFactory pluginFactory = new PluginFactory();
+        return pluginFactory.getGroupPersistence();
+    }
+
+    <T extends Principal> UserPersistence<T> getUserPersistence()
+    {
+        PluginFactory pluginFactory = new PluginFactory();
+        return pluginFactory.getUserPersistence();
+    }
+
+    protected void logGroupInfo(String groupID, List<String> deletedMembers, List<String> addedMembers)
+    {
+        this.logInfo.groupID = groupID;
+        this.logInfo.addedMembers = addedMembers;
+        this.logInfo.deletedMembers = deletedMembers;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java
new file mode 100755
index 0000000000000000000000000000000000000000..8cd6c80d97d076c66207f0ffa68a64aa8b87ccf1
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java
@@ -0,0 +1,177 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.util.StringUtil;
+import java.io.IOException;
+import java.net.URLDecoder;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.log4j.Logger;
+
+public class GroupsActionFactory
+{
+    private static final Logger log = Logger.getLogger(GroupsActionFactory.class);
+
+    static GroupsAction getGroupsAction(HttpServletRequest request, GroupLogInfo logInfo)
+        throws IOException
+    {
+        GroupsAction action = null;
+        String method = request.getMethod();
+        String path = request.getPathInfo();
+        log.debug("method: " + method);
+        log.debug("path: " + path);
+
+        if (path == null)
+        {
+            path = new String();
+        }
+        if (path.startsWith("/"))
+        {
+            path = path.substring(1);
+        }
+        if (path.endsWith("/"))
+        {
+            path = path.substring(0, path.length() - 1);
+        }
+        String[] segments = new String[0];
+        if (StringUtil.hasText(path))
+        {
+            segments = path.split("/");
+        }
+
+        if (segments.length == 0)
+        {
+            if (method.equals("GET"))
+            {
+                action = new ListGroupsAction(logInfo);
+            }
+            else if (method.equals("PUT"))
+            {
+                action = new CreateGroupAction(logInfo, request.getInputStream());
+            }
+
+        }
+        else if (segments.length == 1)
+        {
+            String groupName = segments[0];
+            if (method.equals("GET"))
+            {
+                action = new GetGroupAction(logInfo, groupName);
+            }
+            else if (method.equals("DELETE"))
+            {
+                action = new DeleteGroupAction(logInfo, groupName);
+            }
+            else if (method.equals("POST"))
+            {
+                action = new ModifyGroupAction(logInfo, groupName, request.getInputStream());
+            }
+        }
+        else if (segments.length == 3)
+        {
+            String groupName = segments[0];
+            String memberCategory = segments[1];
+            if (method.equals("PUT"))
+            {
+                if (memberCategory.equals("groupMembers"))
+                {
+                    String groupMemberName = segments[2];
+                    action = new AddGroupMemberAction(logInfo, groupName, groupMemberName);
+                }
+                else if (memberCategory.equals("userMembers"))
+                {
+                    String userMemberID = URLDecoder.decode(segments[2], "UTF-8");
+                    String userMemberIDType = request.getParameter("idType");
+                    action = new AddUserMemberAction(logInfo, groupName, userMemberID, userMemberIDType);
+                }
+            }
+            else if (method.equals("DELETE"))
+            {
+                if (memberCategory.equals("groupMembers"))
+                {
+                    String groupMemberName = segments[2];
+                    action = new RemoveGroupMemberAction(logInfo, groupName, groupMemberName);
+                }
+                else if (memberCategory.equals("userMembers"))
+                {
+                    String memberUserID = URLDecoder.decode(segments[2], "UTF-8");
+                    String memberUserIDType = request.getParameter("idType");
+                    action = new RemoveUserMemberAction(logInfo, groupName, memberUserID, memberUserIDType);
+                }
+            }
+        }
+
+        if (action != null)
+        {
+            return action;
+        }
+        throw new IllegalArgumentException("Bad groups request: " + method + " on " + path);
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsServlet.java
new file mode 100755
index 0000000000000000000000000000000000000000..bd0713eeb638227acab81a36655cb98bb107367d
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsServlet.java
@@ -0,0 +1,158 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.security.auth.Subject;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.log4j.Logger;
+
+public class GroupsServlet extends HttpServlet
+{
+    private static final Logger log = Logger.getLogger(GroupsServlet.class);
+
+    private void doAction(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        long start = System.currentTimeMillis();
+        GroupLogInfo logInfo = new GroupLogInfo(request);
+        try
+        {
+            log.info(logInfo.start());
+
+            Subject subject = AuthenticationUtil.getSubject(request);
+            logInfo.setSubject(subject);
+            GroupsAction action = GroupsActionFactory.getGroupsAction(request, logInfo);
+            action.doAction(subject, response);
+            log.info(logInfo.end());
+        }
+        catch (IllegalArgumentException e)
+        {
+            log.debug(e.getMessage(), e);
+            logInfo.setMessage(e.getMessage());
+            logInfo.setSuccess(false);
+            response.getWriter().write(e.getMessage());
+            response.setStatus(400);
+        }
+        catch (Throwable t)
+        {
+            String message = "Internal Server Error: " + t.getMessage();
+            log.error(message, t);
+            logInfo.setSuccess(false);
+            logInfo.setMessage(message);
+            response.getWriter().write(message);
+            response.setStatus(500);
+        }
+        finally
+        {
+            logInfo.setElapsedTime(System.currentTimeMillis() - start);
+            log.info(logInfo.end());
+        }
+    }
+
+    @Override
+    public void doGet(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        doAction(request, response);
+    }
+
+    @Override
+    public void doPost(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        doAction(request, response);
+    }
+
+    @Override
+    public void doDelete(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        doAction(request, response);
+    }
+
+    @Override
+    public void doPut(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        doAction(request, response);
+    }
+
+    @Override
+    public void doHead(HttpServletRequest request, HttpServletResponse response)
+        throws IOException
+    {
+        doAction(request, response);
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..6755d00970edc5366444d897b5c7457d400a40fd
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java
@@ -0,0 +1,139 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupReader;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import java.io.InputStream;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import javax.servlet.http.HttpServletResponse;
+
+public class ModifyGroupAction extends GroupsAction
+{
+    private String groupName;
+    private InputStream inputStream;
+
+    ModifyGroupAction(GroupLogInfo logInfo, String groupName, InputStream inputStream)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+        this.inputStream = inputStream;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        Group group = GroupReader.read(this.inputStream);
+        Group oldGroup = groupPersistence.getGroup(this.groupName);
+        Group modifiedGroup = groupPersistence.modifyGroup(group);
+        this.response.sendRedirect(modifiedGroup.getID());
+
+        List addedMembers = new ArrayList();
+        for (User member : group.getUserMembers())
+        {
+            if (!oldGroup.getUserMembers().remove(member))
+            {
+                addedMembers.add(member.getUserID().getName());
+            }
+        }
+        for (Group gr : group.getGroupMembers())
+        {
+            if (!oldGroup.getGroupMembers().remove(gr))
+            {
+                addedMembers.add(gr.getID());
+            }
+        }
+        if (addedMembers.isEmpty())
+        {
+            addedMembers = null;
+        }
+        List deletedMembers = new ArrayList();
+        for (User member : oldGroup.getUserMembers())
+        {
+            deletedMembers.add(member.getUserID().getName());
+        }
+        for (Group gr : oldGroup.getGroupMembers())
+        {
+            deletedMembers.add(gr.getID());
+        }
+        if (deletedMembers.isEmpty())
+        {
+            deletedMembers = null;
+        }
+        logGroupInfo(group.getID(), deletedMembers, addedMembers);
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/RemoveGroupMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/RemoveGroupMemberAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..281f06d31b4483e503a1438e3666289a6c7d7cda
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/RemoveGroupMemberAction.java
@@ -0,0 +1,108 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class RemoveGroupMemberAction extends GroupsAction
+{
+    private String groupName;
+    private String groupMemberName;
+
+    RemoveGroupMemberAction(GroupLogInfo logInfo, String groupName, String groupMemberName)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+        this.groupMemberName = groupMemberName;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        Group group = groupPersistence.getGroup(this.groupName);
+        Group toRemove = groupPersistence.getGroup(this.groupMemberName);
+        if (!group.getGroupMembers().remove(toRemove))
+        {
+            throw new GroupNotFoundException(this.groupMemberName);
+        }
+        groupPersistence.modifyGroup(group);
+
+        List deletedMembers = new ArrayList();
+        deletedMembers.add(toRemove.getID());
+        logGroupInfo(group.getID(), deletedMembers, null);
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/RemoveUserMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/RemoveUserMemberAction.java
new file mode 100755
index 0000000000000000000000000000000000000000..d5aa14aef19162c0dd1255203973e4aac3d4d5bd
--- /dev/null
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/RemoveUserMemberAction.java
@@ -0,0 +1,116 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.MemberNotFoundException;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.server.GroupPersistence;
+import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+public class RemoveUserMemberAction extends GroupsAction
+{
+    private String groupName;
+    private String userID;
+    private String userIDType;
+
+    RemoveUserMemberAction(GroupLogInfo logInfo, String groupName, String userID, String userIDType)
+    {
+        super(logInfo);
+        this.groupName = groupName;
+        this.userID = userID;
+        this.userIDType = userIDType;
+    }
+
+    public Object run()
+        throws Exception
+    {
+        GroupPersistence groupPersistence = getGroupPersistence();
+        UserPersistence userPersistence = getUserPersistence();
+        Group group = groupPersistence.getGroup(this.groupName);
+        Principal userPrincipal = AuthenticationUtil.createPrincipal(this.userID, this.userIDType);
+        User toRemove = userPersistence.getUser(userPrincipal);
+        if (!group.getUserMembers().remove(toRemove))
+        {
+            throw new MemberNotFoundException();
+        }
+        groupPersistence.modifyGroup(group);
+
+        List deletedMembers = new ArrayList();
+        deletedMembers.add(toRemove.getUserID().getName());
+        logGroupInfo(group.getID(), deletedMembers, null);
+        return null;
+    }
+
+}
diff --git a/projects/cadcAccessControl/build.xml b/projects/cadcAccessControl/build.xml
index 25762598f1a728a912d6fe99a1a6c27352a4e9cf..8f7af7878aca174b0fc155f8ed9bf21094522850 100644
--- a/projects/cadcAccessControl/build.xml
+++ b/projects/cadcAccessControl/build.xml
@@ -8,7 +8,7 @@
 *  National Research Council            Conseil national de recherches
 *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
 *  All rights reserved                  Tous droits réservés
-*                                       
+*
 *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
 *  expressed, implied, or               énoncée, implicite ou légale,
 *  statutory, of any kind with          de quelque nature que ce
@@ -31,10 +31,10 @@
 *  software without specific prior      de ce logiciel sans autorisation
 *  written permission.                  préalable et particulière
 *                                       par écrit.
-*                                       
+*
 *  This file is part of the             Ce fichier fait partie du projet
 *  OpenCADC project.                    OpenCADC.
-*                                       
+*
 *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
 *  you can redistribute it and/or       vous pouvez le redistribuer ou le
 *  modify it under the terms of         modifier suivant les termes de
@@ -44,7 +44,7 @@
 *  either version 3 of the              : soit la version 3 de cette
 *  License, or (at your option)         licence, soit (à votre gré)
 *  any later version.                   toute version ultérieure.
-*                                       
+*
 *  OpenCADC is distributed in the       OpenCADC est distribué
 *  hope that it will be useful,         dans l’espoir qu’il vous
 *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
@@ -54,7 +54,7 @@
 *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
 *  General Public License for           Générale Publique GNU Affero
 *  more details.                        pour plus de détails.
-*                                       
+*
 *  You should have received             Vous devriez avoir reçu une
 *  a copy of the GNU Affero             copie de la Licence Générale
 *  General Public License along         Publique GNU Affero avec
@@ -67,31 +67,35 @@
 ************************************************************************
 -->
 
-
 <!DOCTYPE project>
 <project default="build" basedir=".">
     <property environment="env"/>
     <property file="local.build.properties" />
-    
+
     <!-- site-specific build properties or overrides of values in opencadc.properties -->
     <property file="${env.CADC_PREFIX}/etc/local.properties" />
-    
+
     <!-- site-specific targets, e.g. install, cannot duplicate those in opencadc.targets.xml -->
     <import file="${env.CADC_PREFIX}/etc/local.targets.xml" optional="true" />
 
     <!-- default properties and targets -->
     <property file="${env.CADC_PREFIX}/etc/opencadc.properties" />
     <import file="${env.CADC_PREFIX}/etc/opencadc.targets.xml"/>
-    
+
     <!-- developer convenience: place for extra targets and properties -->
     <import file="extras.xml" optional="true" />
 
-    <property name="project" value="cadcAccessControl" />
+    <property name="project"    value="cadcAccessControl" />
+
+    <property name="cadcUtil"   value="${lib}/cadcUtil.jar" />
+    
+    <property name="jdom2"      value="${ext.lib}/jdom2.jar" />
+    <property name="log4j"      value="${ext.lib}/log4j.jar" />
+    <property name="unboundid"  value="${ext.lib}/unboundid-ldapsdk-se.jar" />
 
-    <property name="cadcUtil" value="${lib}/cadcUtil.jar" />
 
-    <property name="jars" value="${cadcUtil}:${ext.lib}/log4j.jar" />
-  
+    <property name="jars" value="${cadcUtil}:${jdom2}:${log4j}:${unboundid}" />
+    
     <target name="build" depends="compile">
         <jar jarfile="${build}/lib/${project}.jar"
                     basedir="${build}/class"
@@ -101,7 +105,14 @@
     </target>
 
     <!-- JAR files needed to run the test suite -->
-    <property name="testingJars" value="${build}/class:${jars}:${ext.lib}/junit.jar:${ext.lib}/xerces.jar:${ext.dev}/easymock.jar:${ext.dev}/cglib.jar:${ext.dev}/objenesis.jar:${ext.dev}/asm.jar" />
+    <property name="xerces"     value="${ext.dev}/xerces.jar" />
+    <property name="asm"        value="${ext.dev}/asm.jar" />
+    <property name="cglib"      value="${ext.dev}/cglib.jar" />
+    <property name="easymock"   value="${ext.dev}/easymock.jar" />
+    <property name="junit"      value="${ext.dev}/junit.jar" />
+    <property name="objenesis"  value="${ext.dev}/objenesis.jar" />
+    
+    <property name="testingJars" value="${build}/class:${jars}:${xerces}:${asm}:${cglib}:${easymock}:${junit}:${objenesis}" />
 
     <target name="test" depends="compile-test">
         <echo message="Running test" />
@@ -119,5 +130,5 @@
             <formatter type="plain" usefile="false" />
         </junit>
     </target>
-    
+
 </project>
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ACConstants.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ACConstants.java
new file mode 100755
index 0000000000000000000000000000000000000000..f4f1bdad29a95c78e266d26a84e2d1d097fbf560
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ACConstants.java
@@ -0,0 +1,84 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+public class ACConstants
+{
+
+    public static final String PROPERTY_GROUP_DESCRIPTION = "ivo://ivoa.net/gms#description";
+    public static final String PROPERTY_OWNER_DN = "ivo://ivoa.net/gms#owner_dn";
+    public static final String PROPERTY_USER_DN = "ivo://ivoa.net/gms#user_dn";
+    public static final String PROPERTY_PUBLIC = "ivo://ivoa.net/gms#public";
+    public static final String GMS_SERVICE_URI = "ivo://cadc.nrc.ca/gms";
+    public static final String GROUP_URI = "ivo://cadc.nrc.ca/gms#";
+    public static final String ID_TYPE_X500 = "X500";
+    public static final String ID_TYPE_OPENID = "OpenID";
+    public static final String ID_TYPE_USERNAME = "HTTP";
+    public static final String ID_TYPE_UID = "UID";
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java
index e89798147a80a826b7c03c819ed51ac35f48748a..38432d050427fd3b9152fb4ddf7e2c973fc46dfe 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/Group.java
@@ -1,59 +1,95 @@
 /*
  ************************************************************************
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
  *
- * (c) 2014.                            (c) 2014.
- * National Research Council            Conseil national de recherches
- * Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
- * All rights reserved                  Tous droits reserves
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
  *
- * NRC disclaims any warranties         Le CNRC denie toute garantie
- * expressed, implied, or statu-        enoncee, implicite ou legale,
- * tory, of any kind with respect       de quelque nature que se soit,
- * to the software, including           concernant le logiciel, y com-
- * without limitation any war-          pris sans restriction toute
- * ranty of merchantability or          garantie de valeur marchande
- * fitness for a particular pur-        ou de pertinence pour un usage
- * pose.  NRC shall not be liable       particulier.  Le CNRC ne
- * in any event for any damages,        pourra en aucun cas etre tenu
- * whether direct or indirect,          responsable de tout dommage,
- * special or general, consequen-       direct ou indirect, particul-
- * tial or incidental, arising          ier ou general, accessoire ou
- * from the use of the software.        fortuit, resultant de l'utili-
- *                                      sation du logiciel.
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
  *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
  *
- * @author adriand
- * 
- * @version $Revision: $
- * 
- * 
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
  ************************************************************************
  */
-
 package ca.nrc.cadc.ac;
 
 import java.security.Principal;
+import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
 
 public class Group
 {
     private String groupID;
-
+    
     private User<? extends Principal> owner;
-
+    
     // group's properties
     protected Set<GroupProperty> properties = new HashSet<GroupProperty>();
 
     // group's user members
-    private Set<User<? extends Principal>> userMembers = 
-            new HashSet<User<? extends Principal>>();
+    private Set<User<? extends Principal>> userMembers = new HashSet<User<? extends Principal>>();
+
     // group's group members
     private Set<Group> groupMembers = new HashSet<Group>();
-
+    
     public String description;
+    public Date lastModified;
     
     // Access Control properties
     /**
@@ -61,11 +97,13 @@ public class Group
      * Note: this class does not enforce any access control rules
      */
     public Group groupRead;
+    
     /**
      * group that can read and write details of this group
      * Note: this class does not enforce any access control rules
      */
     public Group groupWrite;
+    
     /**
      * flag that show whether the details of this group are publicly readable
      * Note: this class does not enforce any access control rules
@@ -81,21 +119,21 @@ public class Group
      * @param owner
      *            Owner/Creator of the group.
      */
-    public Group(final String groupID,
-            final User<? extends Principal> owner)
+    public Group(String groupID, User<? extends Principal> owner)
     {
-        if(groupID == null)
+        if (groupID == null)
         {
             throw new IllegalArgumentException("Null groupID");
         }
-        
-        // check for invalid path characters in groupID
-        if(!groupID.matches("^[a-zA-Z0-9\\-\\.~_]*$"))
-            throw new IllegalArgumentException("Invalid group ID " + groupID
-                    + ": may not contain space ( ), slash (/), escape (\\), or percent (%)");
+
+        if (!groupID.matches("^[a-zA-Z0-9\\-\\.~_]*$"))
+        {
+            throw new IllegalArgumentException("Invalid group ID " + groupID +
+                    ": may not contain space ( ), slash (/), escape (\\), or percent (%)");
+        }
 
         this.groupID = groupID;
-        if(owner == null)
+        if (owner == null)
         {
             throw new IllegalArgumentException("Null owner");
         }
@@ -148,14 +186,13 @@ public class Group
         return groupMembers;
     }
 
-
     /* (non-Javadoc)
      * @see java.lang.Object#hashCode()
      */
     @Override
     public int hashCode()
     {
-        return 31  + groupID.hashCode();
+        return 31 + groupID.hashCode();
     }
 
     /* (non-Javadoc)
@@ -183,11 +220,10 @@ public class Group
         }
         return true;
     }
-    
+
     @Override
     public String toString()
     {
         return getClass().getSimpleName() + "[" + groupID + "]";
     }
-
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupAlreadyExistsException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupAlreadyExistsException.java
new file mode 100755
index 0000000000000000000000000000000000000000..a73d2340992f0a7d85c233d13d9ae94f2fb59146
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupAlreadyExistsException.java
@@ -0,0 +1,78 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+public class GroupAlreadyExistsException extends Exception
+{
+
+    public GroupAlreadyExistsException(String message)
+    {
+        super(message);
+    }
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupNotFoundException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupNotFoundException.java
new file mode 100755
index 0000000000000000000000000000000000000000..521ae17bf25bb79e8e011ccdf6d6671c71a3018d
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupNotFoundException.java
@@ -0,0 +1,78 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+public class GroupNotFoundException extends Exception
+{
+
+    public GroupNotFoundException(String message)
+    {
+        super(message);
+    }
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java
index 5ddfa8016e0e75077bf34bac7b7b02b32291d4bc..6d7f3486e72ab46553e39d3bc3435cffa6c4f264 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java
@@ -1,71 +1,71 @@
 /*
-************************************************************************
-*******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
-**************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
-*
-*  (c) 2009.                            (c) 2009.
-*  Government of Canada                 Gouvernement du Canada
-*  National Research Council            Conseil national de recherches
-*  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
-*  All rights reserved                  Tous droits réservés
-*                                       
-*  NRC disclaims any warranties,        Le CNRC dénie toute garantie
-*  expressed, implied, or               énoncée, implicite ou légale,
-*  statutory, of any kind with          de quelque nature que ce
-*  respect to the software,             soit, concernant le logiciel,
-*  including without limitation         y compris sans restriction
-*  any warranty of merchantability      toute garantie de valeur
-*  or fitness for a particular          marchande ou de pertinence
-*  purpose. NRC shall not be            pour un usage particulier.
-*  liable in any event for any          Le CNRC ne pourra en aucun cas
-*  damages, whether direct or           être tenu responsable de tout
-*  indirect, special or general,        dommage, direct ou indirect,
-*  consequential or incidental,         particulier ou général,
-*  arising from the use of the          accessoire ou fortuit, résultant
-*  software.  Neither the name          de l'utilisation du logiciel. Ni
-*  of the National Research             le nom du Conseil National de
-*  Council of Canada nor the            Recherches du Canada ni les noms
-*  names of its contributors may        de ses  participants ne peuvent
-*  be used to endorse or promote        être utilisés pour approuver ou
-*  products derived from this           promouvoir les produits dérivés
-*  software without specific prior      de ce logiciel sans autorisation
-*  written permission.                  préalable et particulière
-*                                       par écrit.
-*                                       
-*  This file is part of the             Ce fichier fait partie du projet
-*  OpenCADC project.                    OpenCADC.
-*                                       
-*  OpenCADC is free software:           OpenCADC est un logiciel libre ;
-*  you can redistribute it and/or       vous pouvez le redistribuer ou le
-*  modify it under the terms of         modifier suivant les termes de
-*  the GNU Affero General Public        la “GNU Affero General Public
-*  License as published by the          License” telle que publiée
-*  Free Software Foundation,            par la Free Software Foundation
-*  either version 3 of the              : soit la version 3 de cette
-*  License, or (at your option)         licence, soit (à votre gré)
-*  any later version.                   toute version ultérieure.
-*                                       
-*  OpenCADC is distributed in the       OpenCADC est distribué
-*  hope that it will be useful,         dans l’espoir qu’il vous
-*  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
-*  without even the implied             GARANTIE : sans même la garantie
-*  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
-*  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
-*  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
-*  General Public License for           Générale Publique GNU Affero
-*  more details.                        pour plus de détails.
-*                                       
-*  You should have received             Vous devriez avoir reçu une
-*  a copy of the GNU Affero             copie de la Licence Générale
-*  General Public License along         Publique GNU Affero avec
-*  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
-*  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
-*                                       <http://www.gnu.org/licenses/>.
-*
-*
-************************************************************************
-*/
-
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
 package ca.nrc.cadc.ac;
 
 /**
@@ -73,7 +73,7 @@ package ca.nrc.cadc.ac;
  *
  */
 public class GroupProperty
-{   
+{
     // The property identifier
     private String key;
     
@@ -82,21 +82,21 @@ public class GroupProperty
     
     // true if the property cannot be modified.
     private boolean readOnly;
-    
 
     /**
      * GroupProperty constructor.
      * 
      * @param key The property key. Cannot be null.
      * @param value The property value.
+     * @param readOnly
      */
     public GroupProperty(String key, Object value, boolean readOnly)
     {
-        if(key == null)
+        if (key == null)
         {
             throw new IllegalArgumentException("Null key");
         }
-        if(value == null)
+        if (value == null)
         {
             throw new IllegalArgumentException("Null value");
         }
@@ -128,9 +128,7 @@ public class GroupProperty
     {
         return readOnly;
     }
-    
 
-    
     /* (non-Javadoc)
      * @see java.lang.Object#hashCode()
      */
@@ -139,7 +137,7 @@ public class GroupProperty
     {
         final int prime = 31;
         int result = 1;
-        result = prime * result + ((key == null) ? 0 : key.hashCode());
+        result = prime * result + (key == null ? 0 : key.hashCode());
         return result;
     }
 
@@ -164,11 +162,10 @@ public class GroupProperty
         GroupProperty other = (GroupProperty) obj;
         return key.equals(other.key);
     }
-    
+
     @Override
     public String toString()
     {
         return getClass().getSimpleName() + "[" + key + ": " + value + "]";
     }
-
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
new file mode 100755
index 0000000000000000000000000000000000000000..9ddf7f4d6e3fd0e3580f86ed76dd3ec20859aa2a
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
@@ -0,0 +1,272 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import ca.nrc.cadc.date.DateUtil;
+import ca.nrc.cadc.xml.XmlUtil;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.List;
+import java.util.Set;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+
+public class GroupReader
+{
+
+    public static Group read(String xml)
+            throws ReaderException, IOException, URISyntaxException
+    {
+        if (xml == null)
+        {
+            throw new IllegalArgumentException("XML must not be null");
+        }
+        return read(new StringReader(xml));
+    }
+
+    public static Group read(InputStream in)
+            throws ReaderException, IOException, URISyntaxException
+    {
+        if (in == null)
+        {
+            throw new IOException("stream closed");
+        }
+        InputStreamReader reader;
+        try
+        {
+            reader = new InputStreamReader(in, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("UTF-8 encoding not supported");
+        }
+        return read(reader);
+    }
+
+    public static Group read(Reader reader)
+            throws ReaderException, IOException, URISyntaxException
+    {
+        if (reader == null)
+        {
+            throw new IllegalArgumentException("reader must not be null");
+        }
+
+        Document document;
+        try
+        {
+            document = XmlUtil.buildDocument(reader);
+        }
+        catch (JDOMException jde)
+        {
+            String error = "XML failed validation: " + jde.getMessage();
+            throw new ReaderException(error, jde);
+        }
+
+        Element root = document.getRootElement();
+
+        String groupElemName = root.getName();
+
+        if (!groupElemName.equalsIgnoreCase("group"))
+        {
+            String error = "Expected group element, found " + groupElemName;
+            throw new ReaderException(error);
+        }
+
+        return parseGroup(root);
+    }
+
+    protected static Group parseGroup(Element groupElement)
+            throws URISyntaxException, ReaderException
+    {
+        String uri = groupElement.getAttributeValue("uri");
+        if (uri == null)
+        {
+            String error = "group missing required uri attribute";
+            throw new ReaderException(error);
+        }
+
+        int index = uri.indexOf("ivo://cadc.nrc.ca/gms#");
+        if (index == -1)
+        {
+            String error = "group uri attribute malformed: " + uri;
+            throw new ReaderException(error);
+        }
+        String groupID = uri.substring("ivo://cadc.nrc.ca/gms#".length());
+
+        Element ownerElement = groupElement.getChild("owner");
+        if (ownerElement == null)
+        {
+            String error = "group missing required owner element";
+            throw new ReaderException(error);
+        }
+
+        Element userElement = ownerElement.getChild("user");
+        if (userElement == null)
+        {
+            String error = "owner missing required user element";
+            throw new ReaderException(error);
+        }
+        User user = UserReader.parseUser(userElement);
+
+        Group group = new Group(groupID, user);
+
+        Element descriptionElement = groupElement.getChild("description");
+        if (descriptionElement != null)
+        {
+            group.description = descriptionElement.getText();
+        }
+
+        Element lastModifiedElement = groupElement.getChild("lastModified");
+        if (lastModifiedElement != null)
+        {
+            try
+            {
+                DateFormat df = DateUtil.getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", DateUtil.UTC);
+                group.lastModified = df.parse(lastModifiedElement.getText());
+            }
+            catch (ParseException e)
+            {
+                String error = "Unable to parse group lastModified because " + e.getMessage();
+
+                throw new ReaderException(error);
+            }
+
+        }
+
+        Element publicReadElement = groupElement.getChild("publicRead");
+        if (publicReadElement != null)
+        {
+            group.publicRead = Boolean.valueOf(publicReadElement.getText());
+        }
+
+        Element propertiesElement = groupElement.getChild("properties");
+        if (propertiesElement != null)
+        {
+            List<Element> propertyElements = propertiesElement.getChildren("property");
+            for (Element propertyElement : propertyElements)
+            {
+                group.getProperties().add(GroupPropertyReader.read(propertyElement));
+            }
+
+        }
+
+        Element groupMembersElement = groupElement.getChild("groupMembers");
+        if (groupMembersElement != null)
+        {
+            List<Element> groupElements = groupMembersElement.getChildren("group");
+            for (Element groupMember : groupElements)
+            {
+                group.getGroupMembers().add(parseGroup(groupMember));
+            }
+
+        }
+
+        Element groupReadElement = groupElement.getChild("groupRead");
+        if (groupReadElement != null)
+        {
+            Element groupReadGroupElement = groupReadElement.getChild("group");
+            if (groupReadGroupElement != null)
+            {
+                group.groupRead = parseGroup(groupReadGroupElement);
+            }
+
+        }
+
+        Element groupWriteElement = groupElement.getChild("groupWrite");
+        if (groupWriteElement != null)
+        {
+            Element groupWriteGroupElement = groupWriteElement.getChild("group");
+            if (groupWriteGroupElement != null)
+            {
+                group.groupWrite = parseGroup(groupWriteGroupElement);
+            }
+
+        }
+
+        Element userMembersElement = groupElement.getChild("userMembers");
+        if (userMembersElement != null)
+        {
+            List<Element> userElements = userMembersElement.getChildren("user");
+            for (Element userMember : userElements)
+            {
+                group.getUserMembers().add(UserReader.parseUser(userMember));
+            }
+        }
+
+        return group;
+    }
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
new file mode 100755
index 0000000000000000000000000000000000000000..c99b7d19e1d85311b920d18e8b6ab1389dd676a9
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
@@ -0,0 +1,215 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import ca.nrc.cadc.date.DateUtil;
+import ca.nrc.cadc.util.StringBuilderWriter;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.util.Set;
+import org.jdom2.Attribute;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+
+public class GroupWriter
+{
+    public static void write(Group group, StringBuilder builder)
+            throws IOException, WriterException
+    {
+        write(group, new StringBuilderWriter(builder));
+    }
+
+    public static void write(Group group, OutputStream out)
+            throws IOException, WriterException
+    {
+        OutputStreamWriter outWriter;
+        try
+        {
+            outWriter = new OutputStreamWriter(out, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("UTF-8 encoding not supported", e);
+        }
+        write(group, new BufferedWriter(outWriter));
+    }
+
+    public static void write(Group group, Writer writer)
+            throws IOException, WriterException
+    {
+        if (group == null)
+        {
+            throw new WriterException("null group");
+        }
+
+        write(getGroupElement(group), writer);
+    }
+
+    public static Element getGroupElement(Group group)
+            throws WriterException
+    {
+        return getGroupElement(group, true);
+    }
+
+    public static Element getGroupElement(Group group, boolean deepCopy)
+            throws WriterException
+    {
+        Element groupElement = new Element("group");
+        String groupURI = "ivo://cadc.nrc.ca/gms#" + group.getID();
+        groupElement.setAttribute(new Attribute("uri", groupURI));
+
+        Element ownerElement = new Element("owner");
+        Element userElement = UserWriter.getUserElement(group.getOwner());
+        ownerElement.addContent(userElement);
+        groupElement.addContent(ownerElement);
+
+        if (deepCopy)
+        {
+            if (group.description != null)
+            {
+                Element descriptionElement = new Element("description");
+                descriptionElement.setText(group.description);
+                groupElement.addContent(descriptionElement);
+            }
+
+            Element publicReadElement = new Element("publicRead");
+            publicReadElement.setText(String.valueOf(group.publicRead));
+            groupElement.addContent(publicReadElement);
+
+            if (group.lastModified != null)
+            {
+                Element lastModifiedElement = new Element("lastModified");
+                DateFormat df = DateUtil.getDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", DateUtil.UTC);
+                lastModifiedElement.setText(df.format(group.lastModified));
+                groupElement.addContent(lastModifiedElement);
+            }
+
+            if (!group.getProperties().isEmpty())
+            {
+                Element propertiesElement = new Element("properties");
+                for (GroupProperty property : group.getProperties())
+                {
+                    propertiesElement.addContent(GroupPropertyWriter.write(property));
+                }
+                groupElement.addContent(propertiesElement);
+            }
+
+            if ((group.getGroupMembers() != null) && (!group.getGroupMembers().isEmpty()))
+            {
+                Element groupMembersElement = new Element("groupMembers");
+                for (Group groupMember : group.getGroupMembers())
+                {
+                    groupMembersElement.addContent(getGroupElement(groupMember, false));
+                }
+                groupElement.addContent(groupMembersElement);
+            }
+
+            if (group.groupRead != null)
+            {
+                Element groupReadElement = new Element("groupRead");
+                groupReadElement.addContent(getGroupElement(group.groupRead, false));
+                groupElement.addContent(groupReadElement);
+            }
+
+            if (group.groupWrite != null)
+            {
+                Element groupWriteElement = new Element("groupWrite");
+                groupWriteElement.addContent(getGroupElement(group.groupWrite, false));
+                groupElement.addContent(groupWriteElement);
+            }
+
+            if ((group.getUserMembers() != null) && (!group.getUserMembers().isEmpty()))
+            {
+                Element userMembersElement = new Element("userMembers");
+                for (User userMember : group.getUserMembers())
+                {
+                    userMembersElement.addContent(UserWriter.getUserElement(userMember));
+                }
+                groupElement.addContent(userMembersElement);
+            }
+        }
+
+        return groupElement;
+    }
+
+    private static void write(Element root, Writer writer)
+            throws IOException
+    {
+        XMLOutputter outputter = new XMLOutputter();
+        outputter.setFormat(Format.getPrettyFormat());
+        outputter.output(new Document(root), writer);
+    }
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsReader.java
new file mode 100755
index 0000000000000000000000000000000000000000..b7a09d2aea0e1993382de8574a706ca3c7492275
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsReader.java
@@ -0,0 +1,162 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import ca.nrc.cadc.xml.XmlUtil;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+
+public class GroupsReader
+{
+
+    public static List<Group> read(String xml)
+            throws ReaderException, IOException, URISyntaxException
+    {
+        if (xml == null)
+        {
+            throw new IllegalArgumentException("XML must not be null");
+        }
+        return read(new StringReader(xml));
+    }
+
+    public static List<Group> read(InputStream in)
+            throws ReaderException, IOException, URISyntaxException
+    {
+        if (in == null)
+        {
+            throw new IOException("stream closed");
+        }
+        InputStreamReader reader;
+        try
+        {
+            reader = new InputStreamReader(in, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("UTF-8 encoding not supported");
+        }
+        return read(reader);
+    }
+
+    public static List<Group> read(Reader reader)
+            throws ReaderException, IOException, URISyntaxException
+    {
+        if (reader == null)
+        {
+            throw new IllegalArgumentException("reader must not be null");
+        }
+
+        Document document;
+        try
+        {
+            document = XmlUtil.buildDocument(reader);
+        }
+        catch (JDOMException jde)
+        {
+            String error = "XML failed validation: " + jde.getMessage();
+            throw new ReaderException(error, jde);
+        }
+
+        Element root = document.getRootElement();
+
+        String groupElemName = root.getName();
+
+        if (!groupElemName.equalsIgnoreCase("groups"))
+        {
+            String error = "Expected groups element, found " + groupElemName;
+            throw new ReaderException(error);
+        }
+
+        return parseGroups(root);
+    }
+
+    protected static List<Group> parseGroups(Element groupsElement)
+            throws URISyntaxException, ReaderException
+    {
+        List groups = new ArrayList();
+
+        List<Element> groupElements = groupsElement.getChildren("group");
+        for (Element groupElement : groupElements)
+        {
+            groups.add(GroupReader.parseGroup(groupElement));
+        }
+
+        return groups;
+    }
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsWriter.java
new file mode 100755
index 0000000000000000000000000000000000000000..44a0d093c56d2c6f33b90794d8174f91297c1964
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsWriter.java
@@ -0,0 +1,70 @@
+package ca.nrc.cadc.ac;
+
+import ca.nrc.cadc.util.StringBuilderWriter;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.List;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+
+public class GroupsWriter
+{
+  public static void write(List<Group> groups, StringBuilder builder)
+    throws IOException, WriterException
+  {
+    write(groups, new StringBuilderWriter(builder));
+  }
+
+  public static void write(List<Group> groups, OutputStream out)
+    throws IOException, WriterException
+  {
+    OutputStreamWriter outWriter;
+    try
+    {
+      outWriter = new OutputStreamWriter(out, "UTF-8");
+    }
+    catch (UnsupportedEncodingException e)
+    {
+      throw new RuntimeException("UTF-8 encoding not supported", e);
+    }
+    write(groups, new BufferedWriter(outWriter));
+  }
+
+  public static void write(List<Group> groups, Writer writer)
+    throws IOException, WriterException
+  {
+    if (groups == null)
+    {
+      throw new WriterException("null groups");
+    }
+
+    write(getGroupsElement(groups), writer);
+  }
+
+  public static Element getGroupsElement(List<Group> groups)
+    throws WriterException
+  {
+    Element groupsElement = new Element("groups");
+
+    for (Group group : groups)
+    {
+      groupsElement.addContent(GroupWriter.getGroupElement(group));
+    }
+
+    return groupsElement;
+  }
+
+  private static void write(Element root, Writer writer)
+    throws IOException
+  {
+    XMLOutputter outputter = new XMLOutputter();
+    outputter.setFormat(Format.getPrettyFormat());
+    outputter.output(new Document(root), writer);
+  }
+}
\ No newline at end of file
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/MemberAlreadyExistsException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/MemberAlreadyExistsException.java
new file mode 100755
index 0000000000000000000000000000000000000000..bacc1393859eb72471ebb8636e9b7a11ec290a8d
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/MemberAlreadyExistsException.java
@@ -0,0 +1,73 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+public class MemberAlreadyExistsException extends Exception
+{
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/MemberNotFoundException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/MemberNotFoundException.java
new file mode 100755
index 0000000000000000000000000000000000000000..3ff660a7068fdde89cbac046cc4f2afa01563c9e
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/MemberNotFoundException.java
@@ -0,0 +1,73 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+public class MemberNotFoundException extends Exception
+{
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java
index a5ca2405f39ea169147858ebeba054c9963e08eb..9ccff7ac2c69fcfea9a437b74bf6c81aa97ad77f 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java
@@ -1,51 +1,84 @@
 /*
  ************************************************************************
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
  *
- * (c) 2014.                            (c) 2014.
- * National Research Council            Conseil national de recherches
- * Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
- * All rights reserved                  Tous droits reserves
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
  *
- * NRC disclaims any warranties         Le CNRC denie toute garantie
- * expressed, implied, or statu-        enoncee, implicite ou legale,
- * tory, of any kind with respect       de quelque nature que se soit,
- * to the software, including           concernant le logiciel, y com-
- * without limitation any war-          pris sans restriction toute
- * ranty of merchantability or          garantie de valeur marchande
- * fitness for a particular pur-        ou de pertinence pour un usage
- * pose.  NRC shall not be liable       particulier.  Le CNRC ne
- * in any event for any damages,        pourra en aucun cas etre tenu
- * whether direct or indirect,          responsable de tout dommage,
- * special or general, consequen-       direct ou indirect, particul-
- * tial or incidental, arising          ier ou general, accessoire ou
- * from the use of the software.        fortuit, resultant de l'utili-
- *                                      sation du logiciel.
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
  *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
  *
- * @author adriand
- * 
- * @version $Revision: $
- * 
- * 
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
  ************************************************************************
  */
-
 package ca.nrc.cadc.ac;
 
 public class PersonalDetails implements UserDetails
 {
     private String firstName;
     private String lastName;
-    private String email;
-    private String address;
-    private String institute;
-    private String city;
-    private String country;
+    public String email;
+    public String address;
+    public String institute;
+    public String city;
+    public String country;
 
-    public PersonalDetails(String firstName, String lastName, String email,
-            String address, String institute, String city, String country)
+    public PersonalDetails(String firstName, String lastName)
     {
         if (firstName == null)
         {
@@ -55,34 +88,9 @@ public class PersonalDetails implements UserDetails
         {
             throw new IllegalArgumentException("null lastName");
         }
-        if (email == null)
-        {
-            throw new IllegalArgumentException("null email");
-        }
 
-        if (address == null)
-        {
-            throw new IllegalArgumentException("null address");
-        }
-        if (institute == null)
-        {
-            throw new IllegalArgumentException("null institute");
-        }
-        if (city == null)
-        {
-            throw new IllegalArgumentException("null city");
-        }
-        if (country == null)
-        {
-            throw new IllegalArgumentException("null country");
-        }
         this.firstName = firstName;
         this.lastName = lastName;
-        this.email = email;
-        this.address = address;
-        this.institute = institute;
-        this.city = city;
-        this.country = country;
     }
 
     public String getFirstName()
@@ -95,45 +103,17 @@ public class PersonalDetails implements UserDetails
         return lastName;
     }
 
-    public String getEmail()
-    {
-        return email;
-    }
-
-    public String getAddress()
-    {
-        return address;
-    }
-
-    public String getInstitute()
-    {
-        return institute;
-    }
-
-    public String getCity()
-    {
-        return city;
-    }
-
-    public String getCountry()
-    {
-        return country;
-    }
-
     /* (non-Javadoc)
-     * @see ca.nrc.cadc.auth.model.UserDetails#hashCode()
+     * 
      */
     @Override
     public int hashCode()
     {
-        final int prime = 31;
+        int prime = 31;
         int result = 1;
-        result = prime * result + address.hashCode();
-        result = prime * result + city.hashCode();
-        result = prime * result + country.hashCode();
-        result = prime * result + email.hashCode();
+
         result = prime * result + firstName.hashCode();
-        result = prime * result + institute.hashCode();
+
         result = prime * result + lastName.hashCode();
         return result;
     }
@@ -163,31 +143,7 @@ public class PersonalDetails implements UserDetails
         {
             return false;
         }
-        if (!lastName.equals(other.lastName))
-        {
-            return false;
-        }
-        if (!email.equals(other.email))
-        {
-            return false;
-        }
-        if (!institute.equals(other.institute))
-        {
-            return false;
-        }
-        if (!address.equals(other.address))
-        {
-            return false;
-        }
-        if (!city.equals(other.city))
-        {
-            return false;
-        }
-        if (!country.equals(other.country))
-        {
-            return false;
-        }
-        return true;
+        return lastName.equals(other.lastName);
     }
 
     /* (non-Javadoc)
@@ -196,8 +152,9 @@ public class PersonalDetails implements UserDetails
     @Override
     public String toString()
     {
-        return getClass().getSimpleName() + "[" + firstName + ", "
-                + lastName + ", " + email + ", " + address + ", "
-                + institute + ", " + city + ", " + country + "]";
+        return getClass().getSimpleName() + "[" + firstName + ", " + 
+               lastName + ", " + email + ", " + address + ", " + 
+               institute + ", " + city + ", " + country + "]";
     }
+
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java
index 12d8dec1b4560d9d3b68a681dc36793d51c81247..d6d2b72d90435760d488f4b3d75177e648382210 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java
@@ -1,37 +1,71 @@
 /*
  ************************************************************************
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
  *
- * (c) 2014.                            (c) 2014.
- * National Research Council            Conseil national de recherches
- * Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
- * All rights reserved                  Tous droits reserves
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
  *
- * NRC disclaims any warranties         Le CNRC denie toute garantie
- * expressed, implied, or statu-        enoncee, implicite ou legale,
- * tory, of any kind with respect       de quelque nature que se soit,
- * to the software, including           concernant le logiciel, y com-
- * without limitation any war-          pris sans restriction toute
- * ranty of merchantability or          garantie de valeur marchande
- * fitness for a particular pur-        ou de pertinence pour un usage
- * pose.  NRC shall not be liable       particulier.  Le CNRC ne
- * in any event for any damages,        pourra en aucun cas etre tenu
- * whether direct or indirect,          responsable de tout dommage,
- * special or general, consequen-       direct ou indirect, particul-
- * tial or incidental, arising          ier ou general, accessoire ou
- * from the use of the software.        fortuit, resultant de l'utili-
- *                                      sation du logiciel.
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
  *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
  *
- * @author adriand
- * 
- * @version $Revision: $
- * 
- * 
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
  ************************************************************************
  */
-
 package ca.nrc.cadc.ac;
 
 /**
@@ -42,7 +76,7 @@ public class PosixDetails implements UserDetails
     private long uid;
     private long gid;
     private String homeDirectory;
-
+    
     /**
      * user login shell
      */
@@ -64,8 +98,9 @@ public class PosixDetails implements UserDetails
         if (homeDirectory == null)
         {
             throw new IllegalArgumentException(
-                    "null home directory in POSIX details");
+                "null home directory in POSIX details");
         }
+
         this.homeDirectory = homeDirectory;
     }
 
@@ -101,11 +136,11 @@ public class PosixDetails implements UserDetails
     @Override
     public int hashCode()
     {
-        final int prime = 31;
+        int prime = 31;
         int result = 1;
-        result = prime * result + (int) (gid ^ (gid >>> 32));
+        result = prime * result + (int) (gid ^ gid >>> 32);
         result = prime * result + homeDirectory.hashCode();
-        result = prime * result + (int) (uid ^ (uid >>> 32));
+        result = prime * result + (int) (uid ^ uid >>> 32);
         return result;
     }
 
@@ -145,8 +180,8 @@ public class PosixDetails implements UserDetails
     @Override
     public String toString()
     {
-        return getClass().getSimpleName() + "[" + uid + ", " + gid + ", "
-                + homeDirectory + "]";
+        return getClass().getSimpleName() + "[" + uid + ", " + 
+               gid + ", " + homeDirectory + "]";
     }
 
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ReaderException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ReaderException.java
new file mode 100755
index 0000000000000000000000000000000000000000..dae488c9a168a9f1dd0d2514b009f9c54bd2d71a
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ReaderException.java
@@ -0,0 +1,85 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import java.io.IOException;
+
+public class ReaderException extends IOException
+{
+    public ReaderException(String message)
+    {
+        super(message);
+    }
+
+    public ReaderException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java
index 3174d0136d7d2fbbc5bdd0a4fa5e758118ae0ded..b14c425185ee9591fd82f048b651b05ab8e44ea5 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/User.java
@@ -1,39 +1,71 @@
 /*
  ************************************************************************
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
  *
- * (c) 2014.                            (c) 2014.
- * National Research Council            Conseil national de recherches
- * Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
- * All rights reserved                  Tous droits reserves
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
  *
- * NRC disclaims any warranties         Le CNRC denie toute garantie
- * expressed, implied, or statu-        enoncee, implicite ou legale,
- * tory, of any kind with respect       de quelque nature que se soit,
- * to the software, including           concernant le logiciel, y com-
- * without limitation any war-          pris sans restriction toute
- * ranty of merchantability or          garantie de valeur marchande
- * fitness for a particular pur-        ou de pertinence pour un usage
- * pose.  NRC shall not be liable       particulier.  Le CNRC ne
- * in any event for any damages,        pourra en aucun cas etre tenu
- * whether direct or indirect,          responsable de tout dommage,
- * special or general, consequen-       direct ou indirect, particul-
- * tial or incidental, arising          ier ou general, accessoire ou
- * from the use of the software.        fortuit, resultant de l'utili-
- *                                      sation du logiciel.
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
  *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
  *
- * @author adriand
- * 
- * @version $Revision: $
- * 
- * 
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
  ************************************************************************
  */
-
-
-
 package ca.nrc.cadc.ac;
 
 import java.security.Principal;
@@ -43,46 +75,42 @@ import java.util.Set;
 public class User<T extends Principal>
 {
     private T userID;
-
-    private Set<Principal> principals = new HashSet<Principal>();
     
+    private Set<Principal> identities = new HashSet<Principal>();
+
     public Set<UserDetails> details = new HashSet<UserDetails>();
-    
-    
-    public User(final T userID)
+
+    public User(T userID)
     {
-        if(userID == null)
+        if (userID == null)
         {
             throw new IllegalArgumentException("null userID");
         }
         this.userID = userID;
     }
-    
-    
-    public Set<Principal> getPrincipals()
+
+    public Set<Principal> getIdentities()
     {
-        return principals;
+        return identities;
     }
-    
+
     public T getUserID()
     {
         return userID;
     }
 
-
     /* (non-Javadoc)
      * @see java.lang.Object#hashCode()
      */
     @Override
     public int hashCode()
     {
-        final int prime = 31;
+        int prime = 31;
         int result = 1;
         result = prime * result + userID.hashCode();
         return result;
     }
 
-
     /* (non-Javadoc)
      * @see java.lang.Object#equals(java.lang.Object)
      */
@@ -101,18 +129,18 @@ public class User<T extends Principal>
         {
             return false;
         }
-        User<?> other = (User<?>) obj;
+        User other = (User) obj;
         if (!userID.equals(other.userID))
         {
             return false;
         }
         return true;
     }
-    
+
     @Override
     public String toString()
     {
         return getClass().getSimpleName() + "[" + userID.getName() + "]";
     }
-    
+
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java
index d03035c1be2377d4de5ebd9174729138006da364..65c3643fc3aee0e8e714ab330ec2a2241efc6ffd 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java
@@ -1,56 +1,89 @@
 /*
  ************************************************************************
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
- *
- * (c) 2014.                            (c) 2014.
- * National Research Council            Conseil national de recherches
- * Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
- * All rights reserved                  Tous droits reserves
- *
- * NRC disclaims any warranties         Le CNRC denie toute garantie
- * expressed, implied, or statu-        enoncee, implicite ou legale,
- * tory, of any kind with respect       de quelque nature que se soit,
- * to the software, including           concernant le logiciel, y com-
- * without limitation any war-          pris sans restriction toute
- * ranty of merchantability or          garantie de valeur marchande
- * fitness for a particular pur-        ou de pertinence pour un usage
- * pose.  NRC shall not be liable       particulier.  Le CNRC ne
- * in any event for any damages,        pourra en aucun cas etre tenu
- * whether direct or indirect,          responsable de tout dommage,
- * special or general, consequen-       direct ou indirect, particul-
- * tial or incidental, arising          ier ou general, accessoire ou
- * from the use of the software.        fortuit, resultant de l'utili-
- *                                      sation du logiciel.
- *
- *
- * @author adriand
- * 
- * @version $Revision: $
- * 
- * 
- ****  C A N A D I A N   A S T R O N O M Y   D A T A   C E N T R E  *****
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
  ************************************************************************
  */
-
 package ca.nrc.cadc.ac;
 
-public interface UserDetails
+public abstract interface UserDetails
 {
-
     /*
      * (non-Javadoc)
      * 
      * @see java.lang.Object#hashCode()
      */
-    public int hashCode();
+    public abstract int hashCode();
 
     /*
      * (non-Javadoc)
      * 
      * @see java.lang.Object#equals(java.lang.Object)
      */
-    public boolean equals(Object obj);
+    public abstract boolean equals(Object paramObject);
 
-    public String toString();
+    public abstract String toString();
 
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserNotFoundException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserNotFoundException.java
new file mode 100755
index 0000000000000000000000000000000000000000..46cf371537c9a554803ad82aaa879f5e8e4fc44a
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserNotFoundException.java
@@ -0,0 +1,78 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+public class UserNotFoundException extends Exception
+{
+    public UserNotFoundException(String message)
+    {
+        super(message);
+    }
+
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java
new file mode 100755
index 0000000000000000000000000000000000000000..4ce506018076a31f6e63460685fbdd92e857239a
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java
@@ -0,0 +1,186 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import ca.nrc.cadc.xml.XmlUtil;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.security.Principal;
+import java.util.List;
+import java.util.Set;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.JDOMException;
+
+public class UserReader
+{
+    public static User read(String xml)
+        throws ReaderException, IOException, URISyntaxException
+    {
+        if (xml == null)
+        {
+            throw new IllegalArgumentException("XML must not be null");
+        }
+        return read(new StringReader(xml));
+    }
+
+    public static User read(InputStream in)
+        throws ReaderException, IOException, URISyntaxException
+    {
+        if (in == null)
+        {
+            throw new IOException("stream closed");
+        }
+        InputStreamReader reader;
+        try
+        {
+            reader = new InputStreamReader(in, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("UTF-8 encoding not supported");
+        }
+        return read(reader);
+    }
+
+    public static User read(Reader reader)
+        throws ReaderException, IOException
+    {
+        if (reader == null)
+        {
+            throw new IllegalArgumentException("reader must not be null");
+        }
+
+        Document document;
+        try
+        {
+            document = XmlUtil.buildDocument(reader);
+        }
+        catch (JDOMException jde)
+        {
+            String error = "XML failed validation: " + jde.getMessage();
+            throw new ReaderException(error, jde);
+        }
+
+        Element root = document.getRootElement();
+
+        return parseUser(root);
+    }
+
+    protected static User<Principal> parseUser(Element userElement)
+        throws ReaderException
+    {
+        Element userIDElement = userElement.getChild("userID");
+        if (userIDElement == null)
+        {
+            String error = "userID element not found in user element";
+            throw new ReaderException(error);
+        }
+
+        Element userIDIdentityElement = userIDElement.getChild("identity");
+        if (userIDIdentityElement == null)
+        {
+            String error = "identity element not found in userID element";
+            throw new ReaderException(error);
+        }
+
+        Principal userID = IdentityReader.read(userIDIdentityElement);
+
+        User user = new User(userID);
+
+        Element identitiesElement = userElement.getChild("identities");
+        if (identitiesElement != null)
+        {
+            List<Element> identityElements = identitiesElement.getChildren("identity");
+            for (Element identityElement : identityElements)
+            {
+                user.getIdentities().add(IdentityReader.read(identityElement));
+            }
+
+        }
+
+        Element detailsElement = userElement.getChild("details");
+        if (detailsElement != null)
+        {
+            List<Element> userDetailsElements = detailsElement.getChildren("userDetails");
+            for (Element userDetailsElement : userDetailsElements)
+            {
+                user.details.add(UserDetailsReader.read(userDetailsElement));
+            }
+        }
+
+        return user;
+    }
+
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java
new file mode 100755
index 0000000000000000000000000000000000000000..db434b5ee61342a105b75770e7ece48cd10c2a79
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java
@@ -0,0 +1,161 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import ca.nrc.cadc.util.StringBuilderWriter;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.security.Principal;
+import java.util.Set;
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+
+public class UserWriter
+{
+    public static void write(User user, StringBuilder builder)
+        throws IOException, WriterException
+    {
+        if (user == null)
+        {
+            throw new WriterException("null User");
+        }
+
+        write(user, new StringBuilderWriter(builder));
+    }
+
+    public static void write(User user, OutputStream out)
+        throws IOException, WriterException
+    {
+        OutputStreamWriter outWriter;
+        try
+        {
+            outWriter = new OutputStreamWriter(out, "UTF-8");
+        }
+        catch (UnsupportedEncodingException e)
+        {
+            throw new RuntimeException("UTF-8 encoding not supported", e);
+        }
+        write(user, new BufferedWriter(outWriter));
+    }
+
+    public static void write(User user, Writer writer)
+        throws IOException, WriterException
+    {
+        write(getUserElement(user), writer);
+    }
+
+    public static Element getUserElement(User user)
+        throws WriterException
+    {
+        Element userElement = new Element("user");
+
+        Element userIDElement = new Element("userID");
+        userIDElement.addContent(IdentityWriter.write(user.getUserID()));
+        userElement.addContent(userIDElement);
+
+        Set<Principal> identities = user.getIdentities();
+        if (!identities.isEmpty())
+        {
+            Element identitiesElement = new Element("identities");
+            for (Principal identity : identities)
+            {
+                identitiesElement.addContent(IdentityWriter.write(identity));
+            }
+            userElement.addContent(identitiesElement);
+        }
+
+        if (!user.details.isEmpty())
+        {
+            Element detailsElement = new Element("details");
+            Set<UserDetails> userDetails = user.details;
+            for (UserDetails userDetail : userDetails)
+            {
+                detailsElement.addContent(UserDetailsWriter.write(userDetail));
+            }
+            userElement.addContent(detailsElement);
+        }
+
+        return userElement;
+    }
+
+    private static void write(Element root, Writer writer)
+        throws IOException
+    {
+        XMLOutputter outputter = new XMLOutputter();
+        outputter.setFormat(Format.getPrettyFormat());
+        outputter.output(new Document(root), writer);
+    }
+
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/WriterException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/WriterException.java
new file mode 100755
index 0000000000000000000000000000000000000000..23a7ca89c22eac662f5d8822feac45aaeee317fc
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/WriterException.java
@@ -0,0 +1,85 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac;
+
+import java.io.IOException;
+
+public class WriterException extends IOException
+{
+    public WriterException(String message)
+    {
+        super(message);
+    }
+
+    public WriterException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
new file mode 100755
index 0000000000000000000000000000000000000000..2c68763fdd2698b0a08799f753c72ad02448ce27
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
@@ -0,0 +1,534 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.client;
+
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.GroupReader;
+import ca.nrc.cadc.ac.GroupWriter;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.auth.SSLUtil;
+import ca.nrc.cadc.net.HttpDownload;
+import ca.nrc.cadc.net.HttpPost;
+import ca.nrc.cadc.net.HttpUpload;
+import ca.nrc.cadc.net.NetUtil;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.AccessControlContext;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+import javax.security.auth.Subject;
+import org.apache.log4j.Logger;
+
+public class GMSClient
+{
+    private static final Logger log = Logger.getLogger(GMSClient.class);
+    public SSLSocketFactory sslSocketFactory;
+    private String baseURL;
+
+    public GMSClient(String baseURL)
+        throws IllegalArgumentException
+    {
+        if (baseURL == null)
+        {
+            throw new IllegalArgumentException("baseURL is required");
+        }
+        try
+        {
+            URL testURL = new URL(baseURL);
+            if (!testURL.getProtocol().equals("https"))
+            {
+                throw new IllegalArgumentException("URL must have HTTPS protocol");
+            }
+        }
+        catch (MalformedURLException e)
+        {
+            throw new IllegalArgumentException("URL is malformed: " + e.getMessage());
+        }
+
+        if (baseURL.endsWith("/"))
+        {
+            this.baseURL = baseURL.substring(0, baseURL.length() - 1);
+        }
+        else
+        {
+            this.baseURL = baseURL;
+        }
+    }
+
+    public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory)
+    {
+        this.sslSocketFactory = sslSocketFactory;
+    }
+
+    public List<Group> getGroups()
+    {
+        throw new UnsupportedOperationException("Not yet implemented");
+    }
+
+    public Group createGroup(Group group)
+        throws GroupAlreadyExistsException, AccessControlException, UserNotFoundException, IOException
+    {
+        URL createGroupURL = new URL(this.baseURL + "/groups");
+        log.debug("createGroupURL request to " + createGroupURL.toString());
+
+        StringBuilder groupXML = new StringBuilder();
+        GroupWriter.write(group, groupXML);
+        log.debug("createGroup: " + groupXML);
+
+        byte[] bytes = groupXML.toString().getBytes("UTF-8");
+        ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+
+        HttpUpload transfer = new HttpUpload(in, createGroupURL);
+        transfer.setSSLSocketFactory(getSSLSocketFactory());
+
+        transfer.run();
+
+        Throwable error = transfer.getThrowable();
+        if (error != null)
+        {
+            log.debug("createGroup throwable", error);
+            if ((transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403))
+            {
+                throw new AccessControlException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 400)
+            {
+                throw new IllegalArgumentException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 409)
+            {
+                throw new GroupAlreadyExistsException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 404)
+            {
+                throw new UserNotFoundException(error.getMessage());
+            }
+            throw new IOException(error);
+        }
+
+        String retXML = transfer.getResponseBody();
+        try
+        {
+            log.debug("createGroup returned: " + groupXML);
+            return GroupReader.read(retXML);
+        }
+        catch (Exception bug)
+        {
+            log.error("Unexpected exception", bug);
+            throw new RuntimeException(bug);
+        }
+    }
+
+    public Group getGroup(String groupName)
+        throws GroupNotFoundException, AccessControlException, IOException
+    {
+        URL getGroupURL = new URL(this.baseURL + "/groups/" + groupName);
+        log.debug("getGroup request to " + getGroupURL.toString());
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        HttpDownload transfer = new HttpDownload(getGroupURL, out);
+
+        transfer.setSSLSocketFactory(getSSLSocketFactory());
+        transfer.run();
+
+        Throwable error = transfer.getThrowable();
+        if (error != null)
+        {
+            log.debug("getGroup throwable", error);
+            if ((transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403))
+            {
+                throw new AccessControlException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 400)
+            {
+                throw new IllegalArgumentException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 404)
+            {
+                throw new GroupNotFoundException(error.getMessage());
+            }
+            throw new IOException(error);
+        }
+
+        try
+        {
+            String groupXML = new String(out.toByteArray(), "UTF-8");
+            log.debug("getGroup returned: " + groupXML);
+            return GroupReader.read(groupXML);
+        }
+        catch (Exception bug)
+        {
+            log.error("Unexpected exception", bug);
+            throw new RuntimeException(bug);
+        }
+    }
+
+    public Group updateGroup(Group group)
+        throws IllegalArgumentException, GroupNotFoundException, AccessControlException, IOException
+    {
+        URL updateGroupURL = new URL(this.baseURL + "/groups/" + group.getID());
+        log.debug("updateGroup request to " + updateGroupURL.toString());
+
+        StringBuilder groupXML = new StringBuilder();
+        GroupWriter.write(group, groupXML);
+        log.debug("updateGroup: " + groupXML);
+
+        HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(), "application/xml", true);
+
+        transfer.setSSLSocketFactory(getSSLSocketFactory());
+        transfer.run();
+
+        Throwable error = transfer.getThrowable();
+        if (error != null)
+        {
+            log.debug("updateGroup throwable", error);
+            if ((transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403))
+            {
+                throw new AccessControlException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 400)
+            {
+                throw new IllegalArgumentException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 404)
+            {
+                throw new GroupNotFoundException(error.getMessage());
+            }
+            throw new IOException(error);
+        }
+
+        String retXML = transfer.getResponseBody();
+        try
+        {
+            log.debug("updateGroup returned: " + groupXML);
+            return GroupReader.read(retXML);
+        }
+        catch (Exception bug)
+        {
+            log.error("Unexpected exception", bug);
+            throw new RuntimeException(bug);
+        }
+    }
+
+    public void deleteGroup(String groupName)
+        throws GroupNotFoundException, AccessControlException, IOException
+    {
+        URL deleteGroupURL = new URL(this.baseURL + "/groups/" + groupName);
+        log.debug("deleteGroup request to " + deleteGroupURL.toString());
+        HttpURLConnection conn = (HttpURLConnection) deleteGroupURL.openConnection();
+        conn.setRequestMethod("DELETE");
+
+        SSLSocketFactory sf = getSSLSocketFactory();
+        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
+        {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLSocketFactory());
+        }
+        int responseCode = conn.getResponseCode();
+        if (responseCode != 200)
+        {
+            String errMessage = NetUtil.getErrorBody(conn);
+            log.debug("deleteGroup response " + responseCode + ": " + errMessage);
+
+            if ((responseCode == 401) || (responseCode == 403))
+            {
+                throw new AccessControlException(errMessage);
+            }
+            if (responseCode == 400)
+            {
+                throw new IllegalArgumentException(errMessage);
+            }
+            if (responseCode == 404)
+            {
+                throw new GroupNotFoundException(errMessage);
+            }
+            throw new IOException(errMessage);
+        }
+    }
+
+    public void addGroupMember(String targetGroupName, String groupMemberName)
+        throws IllegalArgumentException, GroupNotFoundException, AccessControlException, IOException
+    {
+        URL addGroupMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/groupMembers/" + groupMemberName);
+        log.debug("addGroupMember request to " + addGroupMemberURL.toString());
+
+        HttpURLConnection conn = (HttpURLConnection) addGroupMemberURL.openConnection();
+        conn.setRequestMethod("PUT");
+
+        SSLSocketFactory sf = getSSLSocketFactory();
+        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
+        {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLSocketFactory());
+        }
+        int responseCode = conn.getResponseCode();
+        if ((responseCode != 200) && (responseCode != 201))
+        {
+            String errMessage = NetUtil.getErrorBody(conn);
+            log.debug("addGroupMember response " + responseCode + ": " + errMessage);
+
+            if ((responseCode == 401) || (responseCode == 403))
+            {
+                throw new AccessControlException(errMessage);
+            }
+            if (responseCode == 400)
+            {
+                throw new IllegalArgumentException(errMessage);
+            }
+            if (responseCode == 404)
+            {
+                throw new GroupNotFoundException(errMessage);
+            }
+            throw new IOException(errMessage);
+        }
+    }
+
+    public void addUserMember(String targetGroupName, Principal userID)
+        throws GroupNotFoundException, AccessControlException, IOException
+    {
+        String userIDType = AuthenticationUtil.getPrincipalType(userID);
+        String encodedUserID = URLEncoder.encode(userID.toString(), "UTF-8");
+        URL addUserMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/userMembers/" + encodedUserID + "?idType=" + userIDType);
+
+        log.debug("addUserMember request to " + addUserMemberURL.toString());
+
+        HttpURLConnection conn = (HttpURLConnection) addUserMemberURL.openConnection();
+        conn.setRequestMethod("PUT");
+
+        SSLSocketFactory sf = getSSLSocketFactory();
+        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
+        {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLSocketFactory());
+        }
+        int responseCode = conn.getResponseCode();
+        if ((responseCode != 200) && (responseCode != 201))
+        {
+            String errMessage = NetUtil.getErrorBody(conn);
+            log.debug("addUserMember response " + responseCode + ": " + errMessage);
+
+            if ((responseCode == 401) || (responseCode == 403))
+            {
+                throw new AccessControlException(errMessage);
+            }
+            if (responseCode == 400)
+            {
+                throw new IllegalArgumentException(errMessage);
+            }
+            if (responseCode == 404)
+            {
+                throw new GroupNotFoundException(errMessage);
+            }
+            throw new IOException(errMessage);
+        }
+    }
+
+    public void removeGroupMember(String targetGroupName, String groupMemberName)
+        throws GroupNotFoundException, AccessControlException, IOException
+    {
+        URL removeGroupMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/groupMembers/" + groupMemberName);
+        log.debug("removeGroupMember request to " + removeGroupMemberURL.toString());
+
+        HttpURLConnection conn = (HttpURLConnection) removeGroupMemberURL.openConnection();
+        conn.setRequestMethod("DELETE");
+
+        SSLSocketFactory sf = getSSLSocketFactory();
+        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
+        {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLSocketFactory());
+        }
+        int responseCode = conn.getResponseCode();
+        if (responseCode != 200)
+        {
+            String errMessage = NetUtil.getErrorBody(conn);
+            log.debug("removeGroupMember response " + responseCode + ": " + errMessage);
+
+            if ((responseCode == 401) || (responseCode == 403))
+            {
+                throw new AccessControlException(errMessage);
+            }
+            if (responseCode == 400)
+            {
+                throw new IllegalArgumentException(errMessage);
+            }
+            if (responseCode == 404)
+            {
+                throw new GroupNotFoundException(errMessage);
+            }
+            throw new IOException(errMessage);
+        }
+    }
+
+    public void removeUserMember(String targetGroupName, Principal userID)
+        throws GroupNotFoundException, AccessControlException, IOException
+    {
+        String userIDType = AuthenticationUtil.getPrincipalType(userID);
+        String encodedUserID = URLEncoder.encode(userID.toString(), "UTF-8");
+        URL removeUserMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/userMembers/" + encodedUserID + "?idType=" + userIDType);
+
+        log.debug("removeUserMember request to " + removeUserMemberURL.toString());
+
+        HttpURLConnection conn = (HttpURLConnection) removeUserMemberURL.openConnection();
+        conn.setRequestMethod("DELETE");
+
+        SSLSocketFactory sf = getSSLSocketFactory();
+        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
+        {
+            ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLSocketFactory());
+        }
+        int responseCode = conn.getResponseCode();
+        if (responseCode != 200)
+        {
+            String errMessage = NetUtil.getErrorBody(conn);
+            log.debug("removeUserMember response " + responseCode + ": " + errMessage);
+
+            if ((responseCode == 401) || (responseCode == 403))
+            {
+                throw new AccessControlException(errMessage);
+            }
+            if (responseCode == 400)
+            {
+                throw new IllegalArgumentException(errMessage);
+            }
+            if (responseCode == 404)
+            {
+                throw new GroupNotFoundException(errMessage);
+            }
+            throw new IOException(errMessage);
+        }
+    }
+
+    public Collection<Group> searchGroups()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    private SSLSocketFactory getSSLSocketFactory()
+    {
+        if (this.sslSocketFactory == null)
+        {
+            log.debug("initHTTPS: lazy init");
+            AccessControlContext ac = AccessController.getContext();
+            Subject s = Subject.getSubject(ac);
+            this.sslSocketFactory = SSLUtil.getSocketFactory(s);
+        }
+        return this.sslSocketFactory;
+    }
+
+    protected Collection<Group> getCachedGroups()
+    {
+        AccessControlContext acContext = AccessController.getContext();
+        Subject subject = Subject.getSubject(acContext);
+        if (subject != null)
+        {
+            Set groupCredentialSet = subject.getPrivateCredentials(GroupCredentials.class);
+            if ((groupCredentialSet != null) && (groupCredentialSet.size() == 1))
+            {
+                Iterator i = groupCredentialSet.iterator();
+                return ((GroupCredentials) i.next()).groupMemberships;
+            }
+        }
+        return null;
+    }
+
+    protected void setCachedGroups(Collection<Group> groups)
+    {
+        AccessControlContext acContext = AccessController.getContext();
+        Subject subject = Subject.getSubject(acContext);
+        if (subject != null)
+        {
+            GroupCredentials groupCredentials = new GroupCredentials();
+            groupCredentials.groupMemberships.addAll(groups);
+            subject.getPrivateCredentials().add(groupCredentials);
+        }
+    }
+
+    protected class GroupCredentials
+    {
+        Collection<Group> groupMemberships = new ArrayList();
+
+        protected GroupCredentials()
+        {
+        }
+
+    }
+
+}
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 3e48c9a0c5cb5a5446d4645d7470db1951d7b37c..9dcbef14d705d9626f22d37dc6151cb178b1775a 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
@@ -32,8 +32,6 @@
  ************************************************************************
  */
 
-
-
 package ca.nrc.cadc.ac;
 
 import org.apache.log4j.Logger;
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserTest.java
index 0fce989ccfd154bcb79f29134780d0d486ec9a21..0c492bccf69600848b7f5a8bcf312a571a5c7688 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserTest.java
@@ -65,8 +65,7 @@ public class UserTest
         assertEquals(user1, user2);
         assertEquals(user1.hashCode(), user2.hashCode());
 
-        user1.details.add(new PersonalDetails("Joe", "Raymond",
-                "jr@email.com", "123 Street", "CADC", "Victoria", "CA"));
+        user1.details.add(new PersonalDetails("Joe", "Raymond"));
         assertEquals(user1, user2);
         assertEquals(user1.hashCode(), user2.hashCode());
 
@@ -78,7 +77,7 @@ public class UserTest
         assertFalse(user3.equals(user4));
         assertFalse(user3.hashCode() == user4.hashCode());
 
-        user1.getPrincipals().add(new X500Principal("cn=aaa,ou=ca"));
+        user1.getIdentities().add(new X500Principal("cn=aaa,ou=ca"));
         assertEquals(user1, user2);
         assertEquals(user1.hashCode(), user2.hashCode());
 
@@ -93,8 +92,7 @@ public class UserTest
         
         // visual test of toString
         System.out.println(user1);
-        System.out.println(new PersonalDetails("Joe", "Raymond",
-                "jr@email.com", "123 Street", "CADC", "Victoria", "CA"));
+        System.out.println(new PersonalDetails("Joe", "Raymond"));
         System.out.println(new PosixDetails(12, 23,"/home/myhome"));
         
     }
@@ -116,8 +114,7 @@ public class UserTest
         thrown = false;
         try
         {
-            new PersonalDetails(null, "Raymond",
-                    "jr@email.com", "123 Street", "CADC", "Victoria", "CA");
+            new PersonalDetails(null, "Raymond");
         }
         catch(IllegalArgumentException e)
         {
@@ -128,8 +125,7 @@ public class UserTest
         thrown = false;
         try
         {
-            new PersonalDetails("Joe", null,
-                    "jr@email.com", "123 Street", "CADC", "Victoria", "CA");
+            new PersonalDetails("Joe", null);
         }
         catch(IllegalArgumentException e)
         {
@@ -137,65 +133,6 @@ public class UserTest
         }
         assertTrue(thrown);
         
-        thrown = false;
-        try
-        {
-            new PersonalDetails("Joe", "Raymond",
-                    null, "123 Street", "CADC", "Victoria", "CA");
-        }
-        catch(IllegalArgumentException e)
-        {
-            thrown = true;
-        }
-        assertTrue(thrown);
-        
-        thrown = false;
-        try
-        {
-            new PersonalDetails("Joe", "Raymond",
-                    "jr@email.com", null, "CADC", "Victoria", "CA");
-        }
-        catch(IllegalArgumentException e)
-        {
-            thrown = true;
-        }
-        assertTrue(thrown);
-        
-        thrown = false;
-        try
-        {
-            new PersonalDetails("Joe", "Raymond",
-                    "jr@email.com", "123 Street", null, "Victoria", "CA");
-        }
-        catch(IllegalArgumentException e)
-        {
-            thrown = true;
-        }
-        assertTrue(thrown);
-        
-        thrown = false;
-        try
-        {
-            new PersonalDetails("Joe", "Raymond",
-                    "jr@email.com", "123 Street", "CADC", null, "CA");
-        }
-        catch(IllegalArgumentException e)
-        {
-            thrown = true;
-        }
-        assertTrue(thrown);
-        
-        thrown = false;
-        try
-        {
-            new PersonalDetails("Joe", "Raymond",
-                    "jr@email.com", "123 Street", "CADC", "Victoria", null);
-        }
-        catch(IllegalArgumentException e)
-        {
-            thrown = true;
-        }
-        assertTrue(thrown);
         
         thrown = false;
         try