diff --git a/projects/cadcAccessControl/build.xml b/projects/cadcAccessControl/build.xml
index 8f7af7878aca174b0fc155f8ed9bf21094522850..9d71805db6c7d6e5b1a92f1863dbe399c1374c9f 100644
--- a/projects/cadcAccessControl/build.xml
+++ b/projects/cadcAccessControl/build.xml
@@ -105,7 +105,7 @@
     </target>
 
     <!-- JAR files needed to run the test suite -->
-    <property name="xerces"     value="${ext.dev}/xerces.jar" />
+    <property name="xerces"     value="${ext.lib}/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" />
@@ -125,8 +125,17 @@
                 <pathelement path="${build}/test/class"/>
                 <pathelement path="${testingJars}"/>
             </classpath>
-            <test name="ca.nrc.cadc.ac.UserTest" />
             <test name="ca.nrc.cadc.ac.GroupTest" />
+            <test name="ca.nrc.cadc.ac.GroupPropertyTest" />
+            <test name="ca.nrc.cadc.ac.GroupPropertyReaderWriterTest" />
+            <test name="ca.nrc.cadc.ac.GroupReaderWriterTest" />
+            <test name="ca.nrc.cadc.ac.IdentityReaderWriterTest" />
+            <test name="ca.nrc.cadc.ac.PersonalDetailsTest" />
+            <test name="ca.nrc.cadc.ac.PosixDetailsTest" />
+            <test name="ca.nrc.cadc.ac.UserDetailsReaderWriterTest" />
+            <test name="ca.nrc.cadc.ac.UserReaderWriterTest" />
+            <test name="ca.nrc.cadc.ac.UserTest" />
+            
             <formatter type="plain" usefile="false" />
         </junit>
     </target>
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ACConstants.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java
similarity index 99%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/ACConstants.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java
index f4f1bdad29a95c78e266d26a84e2d1d097fbf560..677d7b89bb618929532d7f3f1ebadb80d4231ac5 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/ACConstants.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java
@@ -68,9 +68,8 @@
  */
 package ca.nrc.cadc.ac;
 
-public class ACConstants
+public class AC
 {
-
     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";
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java
index 6d7f3486e72ab46553e39d3bc3435cffa6c4f264..2c4c89aad4ddf08e1e14fcd59f39c3002f20f1ae 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupProperty.java
@@ -74,6 +74,32 @@ package ca.nrc.cadc.ac;
  */
 public class GroupProperty
 {
+    /**
+     * Name of the GroupProperty element.
+     */
+    public static final String NAME = "property";
+    
+    /**
+     * Name of the property key attribute in the GroupProperty element.
+     */
+    public static final String KEY_ATTRIBUTE = "key";
+    
+    /**
+     * Name of the property type attribute in the GroupProperty element.
+     */
+    public static final String TYPE_ATTRIBUTE = "type";
+    
+    /**
+     * Name of the property readOnly attribute in the GroupProperty element.
+     */
+    public static final String READONLY_ATTRIBUTE = "readOnly";
+    
+    /**
+     * Allowed types.
+     */
+    public static final String STRING_TYPE = "String";
+    public static final String INTEGER_TYPE = "Integer";
+    
     // The property identifier
     private String key;
     
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
index 9ddf7f4d6e3fd0e3580f86ed76dd3ec20859aa2a..95ba98a433c05fb0a257df398d3fc973ae8335c4 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
@@ -77,6 +77,7 @@ import java.io.Reader;
 import java.io.StringReader;
 import java.io.UnsupportedEncodingException;
 import java.net.URISyntaxException;
+import java.security.Principal;
 import java.text.DateFormat;
 import java.text.ParseException;
 import java.util.List;
@@ -89,7 +90,7 @@ public class GroupReader
 {
 
     public static Group read(String xml)
-            throws ReaderException, IOException, URISyntaxException
+        throws ReaderException, IOException, URISyntaxException
     {
         if (xml == null)
         {
@@ -99,7 +100,7 @@ public class GroupReader
     }
 
     public static Group read(InputStream in)
-            throws ReaderException, IOException, URISyntaxException
+        throws ReaderException, IOException, URISyntaxException
     {
         if (in == null)
         {
@@ -118,7 +119,7 @@ public class GroupReader
     }
 
     public static Group read(Reader reader)
-            throws ReaderException, IOException, URISyntaxException
+        throws ReaderException, IOException, URISyntaxException
     {
         if (reader == null)
         {
@@ -150,7 +151,7 @@ public class GroupReader
     }
 
     protected static Group parseGroup(Element groupElement)
-            throws URISyntaxException, ReaderException
+        throws URISyntaxException, ReaderException
     {
         String uri = groupElement.getAttributeValue("uri");
         if (uri == null)
@@ -180,7 +181,7 @@ public class GroupReader
             String error = "owner missing required user element";
             throw new ReaderException(error);
         }
-        User user = UserReader.parseUser(userElement);
+        User<? extends Principal> user = UserReader.parseUser(userElement);
 
         Group group = new Group(groupID, user);
 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
index c99b7d19e1d85311b920d18e8b6ab1389dd676a9..bf4eee6d46a17556f208090b8c46644e7260841c 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupWriter.java
@@ -76,6 +76,7 @@ import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
+import java.security.Principal;
 import java.text.DateFormat;
 import java.util.Set;
 import org.jdom2.Attribute;
@@ -87,13 +88,13 @@ import org.jdom2.output.XMLOutputter;
 public class GroupWriter
 {
     public static void write(Group group, StringBuilder builder)
-            throws IOException, WriterException
+        throws IOException, WriterException
     {
         write(group, new StringBuilderWriter(builder));
     }
 
     public static void write(Group group, OutputStream out)
-            throws IOException, WriterException
+        throws IOException, WriterException
     {
         OutputStreamWriter outWriter;
         try
@@ -108,7 +109,7 @@ public class GroupWriter
     }
 
     public static void write(Group group, Writer writer)
-            throws IOException, WriterException
+        throws IOException, WriterException
     {
         if (group == null)
         {
@@ -119,13 +120,13 @@ public class GroupWriter
     }
 
     public static Element getGroupElement(Group group)
-            throws WriterException
+        throws WriterException
     {
         return getGroupElement(group, true);
     }
 
     public static Element getGroupElement(Group group, boolean deepCopy)
-            throws WriterException
+        throws WriterException
     {
         Element groupElement = new Element("group");
         String groupURI = "ivo://cadc.nrc.ca/gms#" + group.getID();
@@ -194,7 +195,7 @@ public class GroupWriter
             if ((group.getUserMembers() != null) && (!group.getUserMembers().isEmpty()))
             {
                 Element userMembersElement = new Element("userMembers");
-                for (User userMember : group.getUserMembers())
+                for (User<? extends Principal> userMember : group.getUserMembers())
                 {
                     userMembersElement.addContent(UserWriter.getUserElement(userMember));
                 }
@@ -206,10 +207,11 @@ public class GroupWriter
     }
 
     private static void write(Element root, Writer writer)
-            throws IOException
+        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
index b7a09d2aea0e1993382de8574a706ca3c7492275..923d2b53234d87124b3697f981b5a42211544b53 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupsReader.java
@@ -149,7 +149,7 @@ public class GroupsReader
     protected static List<Group> parseGroups(Element groupsElement)
             throws URISyntaxException, ReaderException
     {
-        List groups = new ArrayList();
+        List<Group> groups = new ArrayList<Group>();
 
         List<Element> groupElements = groupsElement.getChildren("group");
         for (Element groupElement : groupElements)
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java
index 9ccff7ac2c69fcfea9a437b74bf6c81aa97ad77f..2ff3a7e2564afcc458cd58b495581c27c7f4a68a 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PersonalDetails.java
@@ -70,6 +70,46 @@ package ca.nrc.cadc.ac;
 
 public class PersonalDetails implements UserDetails
 {
+    /**
+     * Name of the PersonalDetails element.
+     */
+    public static final String NAME = "personalDetails";
+    
+    /**
+     * Name of the firstName element.
+     */
+    public static final String FIRSTNAME = "firstName";
+    
+    /**
+     * Name of the lastName element.
+     */
+    public static final String LASTNAME = "lastName";
+    
+    /**
+     * Name of the email element.
+     */
+    public static final String EMAIL = "email";
+    
+    /**
+     * Name of the email element.
+     */
+    public static final String ADDRESS = "address";
+    
+    /**
+     * Name of the email element.
+     */
+    public static final String INSTITUTE = "institute";
+    
+    /**
+     * Name of the email element.
+     */
+    public static final String CITY = "city";
+    
+    /**
+     * Name of the email element.
+     */
+    public static final String COUNTRY = "country";
+        
     private String firstName;
     private String lastName;
     public String email;
@@ -111,9 +151,7 @@ public class PersonalDetails implements UserDetails
     {
         int prime = 31;
         int result = 1;
-
         result = prime * result + firstName.hashCode();
-
         result = prime * result + lastName.hashCode();
         return result;
     }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java
index d6d2b72d90435760d488f4b3d75177e648382210..838ad9d27a55050973c1ad91de3a6766269e411a 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/PosixDetails.java
@@ -73,6 +73,26 @@ package ca.nrc.cadc.ac;
  */
 public class PosixDetails implements UserDetails
 {
+    /**
+     * Name of the PosixDetails element.
+     */
+    public static final String NAME = "posixDetails";
+    
+    /**
+     * Name of the uid element.
+     */
+    public static final String UID = "uid";
+    
+    /**
+     * Name of the gid element.
+     */
+    public static final String GID = "gid";
+    
+    /**
+     * Name of the homeDirectory element.
+     */
+    public static final String HOME_DIRECTORY = "homeDirectory";
+        
     private long uid;
     private long gid;
     private String homeDirectory;
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java
index 65c3643fc3aee0e8e714ab330ec2a2241efc6ffd..92c259f181c9108f7ba6d56a5772022e112e7eee 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserDetails.java
@@ -70,6 +70,16 @@ package ca.nrc.cadc.ac;
 
 public abstract interface UserDetails
 {
+    /**
+     * Name of the UserDetails element.
+     */
+    public static final String NAME = "userDetails";
+    
+    /**
+     * Name of the property type attribute in the UserDetails element.
+     */
+    public static final String TYPE_ATTRIBUTE = "type";
+    
     /*
      * (non-Javadoc)
      * 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java
index 4ce506018076a31f6e63460685fbdd92e857239a..2f6ada0b8279585bd5972b3e0bef6b761314bb36 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserReader.java
@@ -78,14 +78,13 @@ 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)
+    public static User<? extends Principal> read(String xml)
         throws ReaderException, IOException, URISyntaxException
     {
         if (xml == null)
@@ -95,7 +94,7 @@ public class UserReader
         return read(new StringReader(xml));
     }
 
-    public static User read(InputStream in)
+    public static User<? extends Principal> read(InputStream in)
         throws ReaderException, IOException, URISyntaxException
     {
         if (in == null)
@@ -114,7 +113,7 @@ public class UserReader
         return read(reader);
     }
 
-    public static User read(Reader reader)
+    public static User<? extends Principal> read(Reader reader)
         throws ReaderException, IOException
     {
         if (reader == null)
@@ -138,7 +137,7 @@ public class UserReader
         return parseUser(root);
     }
 
-    protected static User<Principal> parseUser(Element userElement)
+    protected static User<? extends Principal> parseUser(Element userElement)
         throws ReaderException
     {
         Element userIDElement = userElement.getChild("userID");
@@ -157,7 +156,7 @@ public class UserReader
 
         Principal userID = IdentityReader.read(userIDIdentityElement);
 
-        User user = new User(userID);
+        User<Principal> user = new User<Principal>(userID);
 
         Element identitiesElement = userElement.getChild("identities");
         if (identitiesElement != null)
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java
index db434b5ee61342a105b75770e7ece48cd10c2a79..4fae569d026fce1f3d15257650ac8f01dec985b7 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserWriter.java
@@ -84,20 +84,15 @@ import org.jdom2.output.XMLOutputter;
 
 public class UserWriter
 {
-    public static void write(User user, StringBuilder builder)
+    public static void write(User<? extends Principal> 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)
+    public static void write(User<? extends Principal> user, OutputStream out)
         throws IOException, WriterException
-    {
+    {                
         OutputStreamWriter outWriter;
         try
         {
@@ -110,13 +105,18 @@ public class UserWriter
         write(user, new BufferedWriter(outWriter));
     }
 
-    public static void write(User user, Writer writer)
+    public static void write(User<? extends Principal> user, Writer writer)
         throws IOException, WriterException
     {
+        if (user == null)
+        {
+            throw new WriterException("null User");
+        }
+        
         write(getUserElement(user), writer);
     }
 
-    public static Element getUserElement(User user)
+    public static Element getUserElement(User<? extends Principal> user)
         throws WriterException
     {
         Element userElement = new Element("user");
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
index 2c68763fdd2698b0a08799f753c72ad02448ce27..3107268993dfc0eced2257a167492fc2b05c1bfd 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
@@ -523,7 +523,7 @@ public class GMSClient
 
     protected class GroupCredentials
     {
-        Collection<Group> groupMemberships = new ArrayList();
+        Collection<Group> groupMemberships = new ArrayList<Group>();
 
         protected GroupCredentials()
         {
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupPropertyReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupPropertyReaderWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..189352538636b4f9561ec7ab2a472012f8fb6f0e
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupPropertyReaderWriterTest.java
@@ -0,0 +1,178 @@
+/*
+ ************************************************************************
+ *******************  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 org.apache.log4j.Logger;
+import org.jdom2.Element;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class GroupPropertyReaderWriterTest
+{
+    private static Logger log = Logger.getLogger(GroupPropertyReaderWriterTest.class);
+
+    @Test
+    public void testReaderExceptions()
+        throws Exception
+    {
+        Element element = null;
+        try
+        {
+            GroupProperty gp = GroupPropertyReader.read(element);
+            fail("null element should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element = new Element("foo");
+        try
+        {
+            GroupProperty gp = GroupPropertyReader.read(element);
+            fail("element not named 'property' should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element = new Element("property");
+        try
+        {
+            GroupProperty gp = GroupPropertyReader.read(element);
+            fail("element without 'key' attribute should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element.setAttribute("key", "foo");
+        try
+        {
+            GroupProperty gp = GroupPropertyReader.read(element);
+            fail("element without 'type' attribute should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element.setAttribute("type", "Double");
+        try
+        {
+            GroupProperty gp = GroupPropertyReader.read(element);
+            fail("Unsupported 'type' should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+    }
+     
+    @Test
+    public void testWriterExceptions()
+        throws Exception
+    {
+        try
+        {
+            Element element = GroupPropertyWriter.write(null);
+            fail("null GroupProperty should throw WriterException");
+        }
+        catch (WriterException e) {}
+         
+        GroupProperty gp = new GroupProperty("key", new Double(1.0), true);
+        try
+        {
+            Element element = GroupPropertyWriter.write(gp);
+            fail("Unsupported GroupProperty type should throw IllegalArgumentException");
+        }
+        catch (IllegalArgumentException e) {}
+    }
+     
+    @Test
+    public void testReadWrite()
+        throws Exception
+    {
+        // String type
+        GroupProperty expected = new GroupProperty("key", "value", true);
+        Element element = GroupPropertyWriter.write(expected);
+        assertNotNull(element);
+         
+        GroupProperty actual = GroupPropertyReader.read(element);
+        assertNotNull(actual);
+         
+        assertEquals(expected, actual);
+         
+        // Integer tuype
+        expected = new GroupProperty("key", new Integer(1), false);
+        element = GroupPropertyWriter.write(expected);
+        assertNotNull(element);
+         
+        actual = GroupPropertyReader.read(element);
+        assertNotNull(actual);
+         
+        assertEquals(expected, actual);
+    }
+     
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupPropertyTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupPropertyTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..b19171c414d64d3d812cc3c55e3ac6bf4709e2be
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupPropertyTest.java
@@ -0,0 +1,128 @@
+/*
+ ************************************************************************
+ *******************  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 org.apache.log4j.Logger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class GroupPropertyTest
+{
+    private static Logger log = Logger.getLogger(GroupPropertyTest.class);
+    
+    @Test
+    public void simpleGroupPropertyTest() throws Exception
+    {
+        GroupProperty gp1 = new GroupProperty("key", "value", false);
+        
+        assertEquals("key", gp1.getKey());
+        assertEquals("value", gp1.getValue());
+        assertEquals(false, gp1.isReadOnly());
+        
+        GroupProperty gp2 = gp1;
+        assertEquals(gp1.hashCode(), gp2.hashCode());
+        assertEquals(gp1, gp2);
+        assertTrue(gp1 == gp2);
+        
+        // test toString
+        System.out.println(gp1);
+    }
+    
+    @Test
+    public void exceptionTests()
+    {
+        boolean thrown = false;
+        try
+        {
+            new GroupProperty(null, "value", true);
+        }
+        catch(IllegalArgumentException e)
+        {
+            thrown = true;
+        }
+        assertTrue(thrown);
+        
+        
+        thrown = false;
+        try
+        {
+            new GroupProperty("key", null, true);
+        }
+        catch(IllegalArgumentException e)
+        {
+            thrown = true;
+        }
+        assertTrue(thrown);
+    }
+    
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e3a239e6289a9e2b6a9454c4eb6d6734d5348b23
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupReaderWriterTest.java
@@ -0,0 +1,170 @@
+/*
+ ************************************************************************
+ *******************  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.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.NumericPrincipal;
+import ca.nrc.cadc.auth.OpenIdPrincipal;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.Principal;
+import java.util.Date;
+import javax.security.auth.x500.X500Principal;
+import org.apache.log4j.Logger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class GroupReaderWriterTest
+{
+    private static Logger log = Logger.getLogger(GroupReaderWriterTest.class);
+
+    @Test
+    public void testReaderExceptions()
+        throws Exception
+    {
+        try
+        {
+            String s = null;
+            Group g = GroupReader.read(s);
+            fail("null String should throw IllegalArgumentException");
+        }
+        catch (IllegalArgumentException e) {}
+        
+        try
+        {
+            InputStream in = null;
+            Group g = GroupReader.read(in);
+            fail("null InputStream should throw IOException");
+        }
+        catch (IOException e) {}
+        
+        try
+        {
+            Reader r = null;
+            Group g = GroupReader.read(r);
+            fail("null element should throw ReaderException");
+        }
+        catch (IllegalArgumentException e) {}
+    }
+     
+    @Test
+    public void testWriterExceptions()
+        throws Exception
+    {
+        try
+        {
+            GroupWriter.write(null, new StringBuilder());
+            fail("null Group should throw WriterException");
+        }
+        catch (WriterException e) {}
+    }
+     
+    @Test
+    public void testReadWrite()
+        throws Exception
+    {
+        Group expected = new Group("groupID", new User<Principal>(new HttpPrincipal("foo")));
+        expected.description = "description";
+        expected.lastModified = new Date();
+        expected.publicRead = true;
+        expected.properties.add(new GroupProperty("key", "value", true));
+        
+        Group readGroup = new Group("read", new User<Principal>(new X500Principal("cn=foo,o=ca")));
+        Group writeGroup = new Group("write", new User<Principal>(new NumericPrincipal(123l)));
+        Group groupMember = new Group("member", new User<Principal>(new OpenIdPrincipal("bar")));
+        User<Principal> userMember = new User<Principal>(new HttpPrincipal("baz"));
+        
+        expected.groupRead = readGroup;
+        expected.groupWrite = writeGroup;
+        expected.getGroupMembers().add(groupMember);
+        expected.getUserMembers().add(userMember);
+        
+        StringBuilder xml = new StringBuilder();
+        GroupWriter.write(expected, xml);
+        assertFalse(xml.toString().isEmpty());
+        
+        Group actual = GroupReader.read(xml.toString());
+        assertNotNull(actual);
+        assertEquals(expected, actual);
+        assertEquals(expected.description, actual.description);
+        assertEquals(expected.lastModified, actual.lastModified);
+        assertEquals(expected.publicRead, actual.publicRead);
+        assertEquals(expected.getProperties(), actual.getProperties());
+        assertEquals(expected.groupRead, actual.groupRead);
+        assertEquals(expected.groupWrite, actual.groupWrite);
+        assertEquals(expected.getGroupMembers(), actual.getGroupMembers());
+        assertEquals(expected.getUserMembers(), actual.getUserMembers());
+    }
+    
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
index 9dcbef14d705d9626f22d37dc6151cb178b1775a..586a0170343ec755e2c24d373120a1c13851633b 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/GroupTest.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;
 
 import org.apache.log4j.Logger;
@@ -158,4 +192,5 @@ public class GroupTest
         }
         assertTrue(thrown);
     }
+    
 }
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/IdentityReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/IdentityReaderWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..cabd9e945345acd6c2aa10aff8b78d5460980cf5
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/IdentityReaderWriterTest.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;
+
+import ca.nrc.cadc.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.NumericPrincipal;
+import ca.nrc.cadc.auth.OpenIdPrincipal;
+import java.security.Principal;
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.x500.X500Principal;
+import org.apache.log4j.Logger;
+import org.jdom2.Element;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class IdentityReaderWriterTest
+{
+    private static Logger log = Logger.getLogger(IdentityReaderWriterTest.class);
+
+    @Test
+    public void testReaderExceptions()
+        throws Exception
+    {
+        Element element = null;
+        try
+        {
+            Principal p = IdentityReader.read(element);
+            fail("null element should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element = new Element("foo");
+        try
+        {
+            Principal p = IdentityReader.read(element);
+            fail("element not named 'identity' should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element = new Element("identity");
+        try
+        {
+            Principal p = IdentityReader.read(element);
+            fail("element without 'type' attribute should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element.setAttribute("type", "foo");
+        try
+        {
+            Principal p = IdentityReader.read(element);
+            fail("element with unknown 'type' attribute should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+    }
+     
+    @Test
+    public void testWriterExceptions()
+        throws Exception
+    {
+        try
+        {
+            Element element = IdentityWriter.write(null);
+            fail("null Identity should throw WriterException");
+        }
+        catch (WriterException e) {}
+         
+        Principal p = new JMXPrincipal("foo");
+        try
+        {
+            Element element = IdentityWriter.write(p);
+            fail("Unsupported Principal type should throw IllegalArgumentException");
+        }
+        catch (IllegalArgumentException e) {}
+    }
+     
+    @Test
+    public void testReadWrite()
+        throws Exception
+    {
+        // X500
+        Principal expected = new X500Principal("cn=foo,o=bar");
+        Element element = IdentityWriter.write(expected);
+        assertNotNull(element);
+         
+        Principal actual = IdentityReader.read(element);
+        assertNotNull(actual);
+         
+        assertEquals(expected, actual);
+         
+        // UID
+        expected = new NumericPrincipal(123l);
+        element = IdentityWriter.write(expected);
+        assertNotNull(element);
+         
+        actual = IdentityReader.read(element);
+        assertNotNull(actual);
+         
+        assertEquals(expected, actual);
+        
+        // OpenID
+        expected = new OpenIdPrincipal("bar");
+        element = IdentityWriter.write(expected);
+        assertNotNull(element);
+         
+        actual = IdentityReader.read(element);
+        assertNotNull(actual);
+         
+        assertEquals(expected, actual);
+        
+        // HTTP
+        expected = new HttpPrincipal("baz");
+        element = IdentityWriter.write(expected);
+        assertNotNull(element);
+         
+        actual = IdentityReader.read(element);
+        assertNotNull(actual);
+         
+        assertEquals(expected, actual);
+    }
+    
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/PersonalDetailsTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/PersonalDetailsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..48e99a6f17b87201d88b06e737072e192fd956ac
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/PersonalDetailsTest.java
@@ -0,0 +1,130 @@
+/*
+ ************************************************************************
+ *******************  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 org.apache.log4j.Logger;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class PersonalDetailsTest
+{
+    private static Logger log = Logger.getLogger(PersonalDetailsTest.class);
+    
+    @Test
+    public void simplePersonalDetailsTest() throws Exception
+    {
+        PersonalDetails pd1 = new PersonalDetails("firstname", "lastname");
+        
+        assertEquals("firstname", pd1.getFirstName());
+        assertEquals("lastname", pd1.getLastName());
+
+        PersonalDetails pd2 = pd1;
+        assertEquals(pd1.hashCode(), pd2.hashCode());
+        assertEquals(pd1, pd2);
+        assertTrue(pd1 == pd2);
+        
+        // test toString
+        System.out.println(pd1);
+    }
+    
+    @Test
+    public void exceptionTests()
+    {
+        boolean thrown = false;
+        try
+        {
+            new PersonalDetails(null, "lastname");
+        }
+        catch(IllegalArgumentException e)
+        {
+            thrown = true;
+        }
+        assertTrue(thrown);
+        
+        
+        thrown = false;
+        try
+        {
+            new PersonalDetails("firstname", null);
+        }
+        catch(IllegalArgumentException e)
+        {
+            thrown = true;
+        }
+        assertTrue(thrown);
+    }    
+    
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/PosixDetailsTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/PosixDetailsTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ba5ecc6a1918edfc8f64d683b6a721e61fd1241b
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/PosixDetailsTest.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;
+
+import org.apache.log4j.Logger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class PosixDetailsTest
+{
+    private static Logger log = Logger.getLogger(PosixDetailsTest.class);
+    
+    @Test
+    public void simplePosixDetailsTest() throws Exception
+    {
+        PosixDetails pd1 = new PosixDetails(1l, 2l, "/dev/null");
+        
+        assertEquals(1l, pd1.getUid());
+        assertEquals(2l, pd1.getGid());
+        assertEquals("/dev/null", pd1.getHomeDirectory());
+        
+        PosixDetails pd2 = pd1;
+        assertEquals(pd1.hashCode(), pd2.hashCode());
+        assertEquals(pd1, pd2);
+        assertTrue(pd1 == pd2);
+        
+        // test toString
+        System.out.println(pd1);
+    }
+    
+    @Test
+    public void exceptionTests()
+    {
+        boolean thrown = false;
+        try
+        {
+            new PosixDetails(1l, 2l, null);
+        }
+        catch(IllegalArgumentException e)
+        {
+            thrown = true;
+        }
+        assertTrue(thrown);
+    }
+    
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/ReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/ReaderWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..68c325e84112744397a4a00b4ed83a60bd8a6e1a
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/ReaderWriterTest.java
@@ -0,0 +1,114 @@
+/*
+ ************************************************************************
+ *******************  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 org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class ReaderWriterTest
+{
+    
+    public ReaderWriterTest()
+    {
+    }
+    
+    @BeforeClass
+    public static void setUpClass()
+    {
+    }
+    
+    @AfterClass
+    public static void tearDownClass()
+    {
+    }
+    
+    @Before
+    public void setUp()
+    {
+    }
+    
+    @After
+    public void tearDown()
+    {
+    }
+
+    // TODO add test methods here.
+    // The methods must be annotated with annotation @Test. For example:
+    //
+    // @Test
+    // public void hello() {}
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserDetailsReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserDetailsReaderWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..159333bdda86d33cb8938558c3dabff602ee1ecc
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserDetailsReaderWriterTest.java
@@ -0,0 +1,175 @@
+/*
+ ************************************************************************
+ *******************  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.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.NumericPrincipal;
+import ca.nrc.cadc.auth.OpenIdPrincipal;
+import java.security.Principal;
+import javax.management.remote.JMXPrincipal;
+import javax.security.auth.x500.X500Principal;
+import org.apache.log4j.Logger;
+import org.jdom2.Element;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class UserDetailsReaderWriterTest
+{
+    private static Logger log = Logger.getLogger(UserDetailsReaderWriterTest.class);
+
+    @Test
+    public void testReaderExceptions()
+        throws Exception
+    {
+        Element element = null;
+        try
+        {
+            UserDetails ud = UserDetailsReader.read(element);
+            fail("null element should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element = new Element("foo");
+        try
+        {
+            UserDetails ud = UserDetailsReader.read(element);
+            fail("element not named 'userDetails' should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element = new Element(UserDetails.NAME);
+        try
+        {
+            UserDetails ud = UserDetailsReader.read(element);
+            fail("element without 'type' attribute should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+         
+        element.setAttribute("type", "foo");
+        try
+        {
+            UserDetails ud = UserDetailsReader.read(element);
+            fail("element with unknown 'type' attribute should throw ReaderException");
+        }
+        catch (ReaderException e) {}
+    }
+     
+    @Test
+    public void testWriterExceptions()
+        throws Exception
+    {
+        try
+        {
+            Element element = UserDetailsWriter.write(null);
+            fail("null UserDetails should throw WriterException");
+        }
+        catch (WriterException e) {}
+    }
+     
+    @Test
+    public void testReadWritePersonalDetails()
+        throws Exception
+    {
+        PersonalDetails expected = new PersonalDetails("firstname", "lastname");
+        expected.address = "address";
+        expected.city = "city";
+        expected.country = "country";
+        expected.email = "email";
+        expected.institute = "institute";
+        Element element = UserDetailsWriter.write(expected);
+        assertNotNull(element);
+        
+        PersonalDetails actual = (PersonalDetails) UserDetailsReader.read(element);
+        assertNotNull(actual);
+        assertEquals(expected, actual);
+        assertEquals(expected.address, actual.address);
+        assertEquals(expected.city, actual.city);
+        assertEquals(expected.country, actual.country);
+        assertEquals(expected.email, actual.email);
+        assertEquals(expected.institute, actual.institute);
+    }
+    
+    @Test
+    public void testReadWritePosixDetails()
+        throws Exception
+    {
+        UserDetails expected = new PosixDetails(123l, 456, "/dev/null");
+        Element element = UserDetailsWriter.write(expected);
+        assertNotNull(element);
+        
+        UserDetails actual = UserDetailsReader.read(element);
+        assertNotNull(actual);
+        assertEquals(expected, actual);
+    }
+    
+}
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserReaderWriterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5f4d3e8d31debc38a2add8a522553985260fd552
--- /dev/null
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserReaderWriterTest.java
@@ -0,0 +1,147 @@
+/*
+ ************************************************************************
+ *******************  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.auth.HttpPrincipal;
+import ca.nrc.cadc.auth.NumericPrincipal;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.security.Principal;
+import org.apache.log4j.Logger;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class UserReaderWriterTest
+{
+    private static Logger log = Logger.getLogger(UserReaderWriterTest.class);
+
+    @Test
+    public void testReaderExceptions()
+        throws Exception
+    {
+        try
+        {
+            String s = null;
+            User<? extends Principal> u = UserReader.read(s);
+            fail("null String should throw IllegalArgumentException");
+        }
+        catch (IllegalArgumentException e) {}
+        
+        try
+        {
+            InputStream in = null;
+            User<? extends Principal> u = UserReader.read(in);
+            fail("null InputStream should throw IOException");
+        }
+        catch (IOException e) {}
+        
+        try
+        {
+            Reader r = null;
+            User<? extends Principal> u = UserReader.read(r);
+            fail("null Reader should throw IllegalArgumentException");
+        }
+        catch (IllegalArgumentException e) {}
+    }
+     
+    @Test
+    public void testWriterExceptions()
+        throws Exception
+    {
+        try
+        {
+            UserWriter.write(null, new StringBuilder());
+            fail("null User should throw WriterException");
+        }
+        catch (WriterException e) {}
+    }
+     
+    @Test
+    public void testReadWrite()
+        throws Exception
+    {
+        User<? extends Principal> expected = new User<Principal>(new HttpPrincipal("foo"));
+        expected.getIdentities().add(new NumericPrincipal(123l));
+        expected.details.add(new PersonalDetails("firstname", "lastname"));
+        
+        StringBuilder xml = new StringBuilder();
+        UserWriter.write(expected, xml);
+        assertFalse(xml.toString().isEmpty());
+        
+        User<? extends Principal> actual = UserReader.read(xml.toString());
+        assertNotNull(actual);
+        assertEquals(expected, actual);
+    }
+    
+}
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 0c492bccf69600848b7f5a8bcf312a571a5c7688..8d8a802704ad9bb4fcf2220ae858088e4dceda71 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserTest.java
@@ -1,38 +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;
+ */package ca.nrc.cadc.ac;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;