diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml
index 0630318fe0877df1c73754d6d9b5f658131598f6..6f8dc7a662b3fc95e0a9a4c1c401868a507ccf83 100644
--- a/projects/cadcAccessControl-Server/build.xml
+++ b/projects/cadcAccessControl-Server/build.xml
@@ -67,83 +67,70 @@
 ************************************************************************
 -->
 
-
-<!DOCTYPE project>
 <project name="cadcAccessControl-Server" 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="cadcAccessControl"   value="${lib}/cadcAccessControl.jar" />
-    <property name="cadcLog"             value="${lib}/cadcLog.jar" />
-    <property name="cadcRegistry"        value="${lib}/cadcRegistryClient.jar" />
-    <property name="cadcUtil"            value="${lib}/cadcUtil.jar" />
-    <property name="cadcUWS"             value="${lib}/cadcUWS.jar" />
-    <property name="wsUtil"              value="${lib}/wsUtil.jar" />
-
-    <property name="javacsv"             value="${ext.lib}/javacsv.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="xerces"              value="${ext.lib}/xerces.jar" />
-
-    <property name="jars" value="${javacsv}:${jdom2}:${log4j}:${servlet}:${unboundid}:${xerces}:${cadcAccessControl}:${cadcLog}:${cadcRegistry}:${cadcUtil}:${cadcUWS}:${wsUtil}" />
-
-    <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="json"           value="${ext.lib}/json.jar" />
-    <property name="easyMock"       value="${ext.dev}/easymock.jar" />
-    <property name="junit"          value="${ext.dev}/junit.jar" />
-    <property name="xmlunit"        value="${ext.dev}/xmlunit.jar" />
-    <property name="cglib"          value="${ext.dev}/cglib.jar" />
-    <property name="objenesis"      value="${ext.dev}/objenesis.jar" />
-    <property name="asm"            value="${ext.dev}/asm.jar" />
-
-    <property name="testingJars"    value="${jars}:${json}:${easyMock}:${junit}:${xmlunit}:${cglib}:${asm}:${objenesis}" />
-
-    <target name="resources">
-        <copy todir="${build}/class">
-            <fileset dir="config">
-                <include name="**.properties" />
-            </fileset>
-        </copy>
-    </target>
 
-    <target name="test" depends="compile,compile-test,resources">
-        <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.server.ldap.LdapUserDAOTest" />
-            <!--<test name="ca.nrc.cadc.ac.server.web.users.UserActionFactoryTest" />-->
-            <!--<test name="ca.nrc.cadc.ac.server.web.users.UsersActionTest" />-->
-            <formatter type="plain" usefile="false" />
-        </junit>
-    </target>
+  <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="cadcAccessControl" value="${lib}/cadcAccessControl.jar"/>
+  <property name="cadcLog" value="${lib}/cadcLog.jar"/>
+  <property name="cadcRegistry" value="${lib}/cadcRegistryClient.jar"/>
+  <property name="cadcUtil" value="${lib}/cadcUtil.jar"/>
+  <property name="cadcUWS" value="${lib}/cadcUWS.jar"/>
+  <property name="wsUtil" value="${lib}/wsUtil.jar"/>
+
+  <property name="javacsv" value="${ext.lib}/javacsv.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="xerces" value="${ext.lib}/xerces.jar"/>
+
+  <property name="jars"
+            value="${javacsv}:${jdom2}:${log4j}:${servlet}:${unboundid}:${xerces}:${cadcAccessControl}:${cadcLog}:${cadcRegistry}:${cadcUtil}:${cadcUWS}:${wsUtil}"/>
+
+  <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="json" value="${ext.lib}/json.jar"/>
+  <property name="easyMock" value="${ext.dev}/easymock.jar"/>
+  <property name="junit" value="${ext.dev}/junit.jar"/>
+  <property name="xmlunit" value="${ext.dev}/xmlunit.jar"/>
+  <property name="cglib" value="${ext.dev}/cglib.jar"/>
+  <property name="objenesis" value="${ext.dev}/objenesis.jar"/>
+  <property name="asm" value="${ext.dev}/asm.jar"/>
+
+  <property name="testingJars"
+            value="${jars}:${json}:${easyMock}:${junit}:${xmlunit}:${cglib}:${asm}:${objenesis}"/>
+
+  <target name="setup-test">
+    <echo>******************</echo>
+    <echo>******************</echo>
+    <echo>Don't forget to set the ca.nrc.cadc.util.PropertiesReader.dir system property first!</echo>
+    <echo>e.g. ant -Dca.nrc.cadc.util.PropertiesReader.dir=test clean build test</echo>
+    <echo>******************</echo>
+    <echo>******************</echo>
+  </target>
 
 </project>
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
index caa18b20c505d98444c4b428ca8a8a9dad7b0aba..595e7dcf1f5d5d4f6f0ac7afae3013d0c21d385f 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/PluginFactory.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/PluginFactory.java
@@ -140,9 +140,10 @@ public class PluginFactory
     @SuppressWarnings("unchecked")
     public <T extends Principal> UserPersistence<T> getUserPersistence()
     {
-        UserPersistence<T> ret = null;
+        UserPersistence ret = null;
         String name = UserPersistence.class.getName();
         String cname = config.getProperty(name);
+
         if (cname == null)
         {
             ret = new LdapUserPersistence<T>();
@@ -152,7 +153,7 @@ public class PluginFactory
             try
             {
                 Class<?> c = Class.forName(cname);
-                ret = (UserPersistence<T>) c.newInstance();
+                ret = (UserPersistence) c.newInstance();
             }
             catch (Exception ex)
             {
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
index 9ae4d37f108ca3f3eb7ed11d5ab966c8e3f4ddeb..af5a6c53cdeabe0bcaed87643e93187f75ada658 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java
@@ -79,7 +79,7 @@ import ca.nrc.cadc.net.TransientException;
 
 import com.unboundid.ldap.sdk.DN;
 
-public abstract interface UserPersistence<T extends Principal>
+public interface UserPersistence<T extends Principal>
 {
     /**
      * Get all user names.
@@ -88,7 +88,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public Collection<String> getUserNames()
+    Collection<String> getUserNames()
             throws TransientException, AccessControlException;
     
     /**
@@ -101,7 +101,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract User<T> addUser(UserRequest<T> user)
+    User<T> addUser(UserRequest<T> user)
         throws TransientException, AccessControlException;
     
     /**
@@ -115,7 +115,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract User<T> getUser(T userID)
+    User<T> getUser(T userID)
         throws UserNotFoundException, TransientException, 
                AccessControlException;
     
@@ -130,7 +130,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract User<T> modifyUser(User<T> user)
+    User<T> modifyUser(User<T> user)
         throws UserNotFoundException, TransientException, 
                AccessControlException;
     
@@ -143,7 +143,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract void deleteUser(T userID)
+    void deleteUser(T userID)
         throws UserNotFoundException, TransientException, 
                AccessControlException;
     
@@ -160,7 +160,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract Collection<DN> getUserGroups(T userID, boolean isAdmin)
+    Collection<DN> getUserGroups(T userID, boolean isAdmin)
         throws UserNotFoundException, TransientException,
                AccessControlException;
     
@@ -176,7 +176,7 @@ public abstract interface UserPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract boolean isMember(T userID, String groupID)
+    boolean isMember(T userID, String groupID)
         throws UserNotFoundException, TransientException,
                AccessControlException;
 }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java
index ad3c1b0be764f0f490d8a0a47c1b03d057c4a7b7..81d25f43c80b34d1917a919930cce9af6e61d655 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java
@@ -77,7 +77,7 @@ import ca.nrc.cadc.ac.xml.UserWriter;
 
 import java.security.Principal;
 
-public class CreateUserAction extends UsersAction
+public class CreateUserAction<T extends Principal> extends UsersAction
 {
     private final InputStream inputStream;
 
@@ -90,11 +90,10 @@ public class CreateUserAction extends UsersAction
     public Object run()
         throws Exception
     {
-        UserPersistence userPersistence = getUserPersistence();
-        UserRequest userRequest = UserRequestReader.read(this.inputStream);
-        User<? extends Principal> newUser = userPersistence.addUser(userRequest);
-        this.response.setContentType(acceptedContentType);
-        writeUser(newUser, this.response.getWriter());
+        UserPersistence<Principal> userPersistence = getUserPersistence();
+        UserRequest<Principal> userRequest = readUserRequest(this.inputStream);
+        User<Principal> newUser = userPersistence.addUser(userRequest);
+        writeUser(newUser);
         logUserInfo(newUser.getUserID().getName());
         return null;
     }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java
index 22f9cf6306bb2ef555e8310de71a4b4506bad74a..d4ee1d0efe03a6bf8fecc24790caa7e0636f07e4 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java
@@ -69,10 +69,10 @@
 
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.UserPersistence;
-import ca.nrc.cadc.ac.xml.UserWriter;
 
 import java.security.Principal;
 
+
 public class GetUserAction extends UsersAction
 {
     private final Principal userID;
@@ -83,13 +83,11 @@ public class GetUserAction extends UsersAction
         this.userID = userID;
     }
 
-    public Object run()
-        throws Exception
+    public Object run() throws Exception
     {
-        UserPersistence userPersistence = getUserPersistence();
-        User<? extends Principal> user = userPersistence.getUser(userID);
-        this.response.setContentType(acceptedContentType);
-        writeUser(user, this.response.getWriter());
+        UserPersistence<Principal> userPersistence = getUserPersistence();
+        User<Principal> user = userPersistence.getUser(userID);
+        writeUser(user);
         return null;
     }
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java
index 5453746623b4a2bf9f3513967a28ee776ac383c1..3a83c96e67cbca4221a40787f16bd00d796be666 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java
@@ -69,7 +69,7 @@
 package ca.nrc.cadc.ac.server.web.users;
 
 import java.io.IOException;
-import java.io.OutputStream;
+import java.io.InputStream;
 import java.io.Writer;
 import java.security.AccessControlException;
 import java.security.Principal;
@@ -80,6 +80,7 @@ import javax.security.auth.Subject;
 import javax.servlet.http.HttpServletResponse;
 
 import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserRequest;
 import org.apache.log4j.Logger;
 
 import ca.nrc.cadc.ac.UserNotFoundException;
@@ -200,6 +201,7 @@ public abstract class UsersAction
         }
     }
 
+    @SuppressWarnings("unchecked")
     <T extends Principal> UserPersistence<T> getUserPersistence()
     {
         PluginFactory pluginFactory = new PluginFactory();
@@ -216,10 +218,80 @@ public abstract class UsersAction
         this.acceptedContentType = acceptedContentType;
     }
 
-    protected final <T extends Principal> void writeUser(final User<T> user,
-                                                         final Writer writer)
+    /**
+     * Read a user request (User pending approval) from the HTTP Request's
+     * stream.
+     *
+     * @param inputStream           The Input Stream to read from.
+     * @return                      User Request instance.
+     * @throws IOException          Any reading errors.
+     */
+    protected final UserRequest<Principal> readUserRequest(
+            final InputStream inputStream) throws IOException
+    {
+        final UserRequest<Principal> userRequest;
+
+        if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
+        {
+            userRequest = ca.nrc.cadc.ac.xml.UserRequestReader.read(inputStream);
+        }
+        else if (acceptedContentType.equals(JSON_CONTENT_TYPE))
+        {
+            userRequest =
+                    ca.nrc.cadc.ac.json.UserRequestReader.read(inputStream);
+        }
+        else
+        {
+            // Should never happen.
+            throw new IOException("Unknown content being asked for: "
+                                  + acceptedContentType);
+        }
+
+        return userRequest;
+    }
+
+    /**
+     * Read a user from the HTTP Request's stream.
+     *
+     * @param inputStream       The Input Stream to read from.
+     * @return                  User instance.
+     * @throws IOException      Any reading error(s)
+     */
+    protected final User<Principal> readUser(
+            final InputStream inputStream) throws IOException
+    {
+        final User<Principal> user;
+
+        if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
+        {
+            user = ca.nrc.cadc.ac.xml.UserReader.read(inputStream);
+        }
+        else if (acceptedContentType.equals(JSON_CONTENT_TYPE))
+        {
+            user = ca.nrc.cadc.ac.json.UserReader.read(inputStream);
+        }
+        else
+        {
+            // Should never happen.
+            throw new IOException("Unknown content being asked for: "
+                                  + acceptedContentType);
+        }
+
+        return user;
+    }
+
+    /**
+     * Write a user to the response's writer.
+     *
+     * @param user              The user object to marshall and write out.
+     * @throws IOException      Any writing errors.
+     */
+    protected final <T extends Principal> void writeUser(final User<T> user)
             throws IOException
     {
+        response.setContentType(acceptedContentType);
+        final Writer writer = response.getWriter();
+
         if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
         {
             ca.nrc.cadc.ac.xml.UserWriter.write(user, writer);
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java
index d97c4b73584a07d302debc1f7a16df9293e95f12..d1f1a9b4f5fd65f36d5bc8cadb214cc89c15359a 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java
@@ -75,28 +75,23 @@ import ca.nrc.cadc.auth.HttpPrincipal;
 import ca.nrc.cadc.auth.NumericPrincipal;
 import ca.nrc.cadc.auth.OpenIdPrincipal;
 import ca.nrc.cadc.util.StringUtil;
+
 import java.io.IOException;
 import java.net.URL;
 import java.security.Principal;
 import javax.security.auth.x500.X500Principal;
 import javax.servlet.http.HttpServletRequest;
+
 import org.apache.log4j.Logger;
 
-import ca.nrc.cadc.ac.server.web.AddGroupMemberAction;
-import ca.nrc.cadc.ac.server.web.AddUserMemberAction;
-import ca.nrc.cadc.ac.server.web.DeleteGroupAction;
-import ca.nrc.cadc.ac.server.web.GetGroupAction;
-import ca.nrc.cadc.ac.server.web.ModifyGroupAction;
-import ca.nrc.cadc.ac.server.web.RemoveGroupMemberAction;
-import ca.nrc.cadc.ac.server.web.RemoveUserMemberAction;
-import ca.nrc.cadc.util.StringUtil;
 
 public class UsersActionFactory
 {
-    private static final Logger log = Logger.getLogger(UsersActionFactory.class);
+    private static final Logger log = Logger
+            .getLogger(UsersActionFactory.class);
 
     static UsersAction getUsersAction(HttpServletRequest request, UserLogInfo logInfo)
-        throws IOException
+            throws IOException
     {
         UsersAction action = null;
         String method = request.getMethod();
@@ -133,7 +128,8 @@ public class UsersActionFactory
             }
             else if (method.equals("PUT"))
             {
-                action = new CreateUserAction(logInfo, request.getInputStream());
+                action = new CreateUserAction(logInfo, request
+                        .getInputStream());
             }
 
         }
@@ -151,7 +147,8 @@ public class UsersActionFactory
             }
             else if (method.equals("POST"))
             {
-                final URL requestURL = new URL(request.getRequestURL().toString());
+                final URL requestURL = new URL(request.getRequestURL()
+                                                       .toString());
                 final StringBuilder sb = new StringBuilder();
                 sb.append(requestURL.getProtocol());
                 sb.append("://");
@@ -166,7 +163,8 @@ public class UsersActionFactory
                 sb.append("/");
                 sb.append(path);
 
-                action = new ModifyUserAction(logInfo, user.getUserID(), sb.toString(),
+                action = new ModifyUserAction(logInfo, user.getUserID(), sb
+                        .toString(),
                                               request.getInputStream());
             }
         }
@@ -180,8 +178,10 @@ public class UsersActionFactory
         throw new IllegalArgumentException(error);
     }
 
-    private static User<? extends Principal> getUser(final String userName, final String idType,
-                                                     final String method, final String path)
+    private static User<? extends Principal> getUser(final String userName,
+                                                     final String idType,
+                                                     final String method,
+                                                     final String path)
     {
         if (idType == null || idType.isEmpty())
         {
@@ -189,27 +189,28 @@ public class UsersActionFactory
         }
         else if (idType.equals(IdentityType.USERNAME.getValue()))
         {
-            return new User(new HttpPrincipal(userName));
+            return new User<HttpPrincipal>(new HttpPrincipal(userName));
         }
         else if (idType.equals(IdentityType.X500.getValue()))
         {
-            return new User(new X500Principal(userName));
+            return new User<X500Principal>(new X500Principal(userName));
         }
         else if (idType.equals(IdentityType.UID.getValue()))
         {
-            return new User(new NumericPrincipal(Long.parseLong(userName)));
+            return new User<NumericPrincipal>(new NumericPrincipal(
+                    Long.parseLong(userName)));
         }
         else if (idType.equals(IdentityType.OPENID.getValue()))
         {
-            return new User(new OpenIdPrincipal(userName));
+            return new User<OpenIdPrincipal>(new OpenIdPrincipal(userName));
         }
         else if (idType.equals(IdentityType.COOKIE.getValue()))
         {
-            return new User(new CookiePrincipal(userName));
+            return new User<CookiePrincipal>(new CookiePrincipal(userName));
         }
         else
         {
-            final String error = "Bad users request: " + method + " on " + path + 
+            final String error = "Bad users request: " + method + " on " + path +
                                  " because of unknown principal type " + idType;
             throw new IllegalArgumentException(error);
         }
diff --git a/projects/cadcAccessControl-Server/test/LdapConfig.test.properties b/projects/cadcAccessControl-Server/test/LdapConfig.test.properties
index d576adc7e06cd7829edc3d1b980dae43c2795c95..0d3bc21ab631c1065379ffda2e5f38f005ed90b4 100644
--- a/projects/cadcAccessControl-Server/test/LdapConfig.test.properties
+++ b/projects/cadcAccessControl-Server/test/LdapConfig.test.properties
@@ -3,6 +3,7 @@ server = proc5-03.cadc.dao.nrc.ca
 port = 636
 proxyUser = webproxy
 usersDn = ou=Users,ou=ds,dc=canfar,dc=net
+userRequestsDN = ou=UserRequests,ou=ds,dc=canfar,dc=net
 newUsersDn = ou=NewUsers,ou=ds,dc=canfar,dc=net
 groupsDn = ou=Groups,ou=ds,dc=canfar,dc=net
 adminGroupsDn = ou=adminGroups,ou=ds,dc=canfar,dc=net
\ No newline at end of file
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..e116d69fc410b6e5c740bec4259f440753f03bd7
--- /dev/null
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java
@@ -0,0 +1,164 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2015.                            (c) 2015.
+ *  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/>.
+ *
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web.users;
+
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.auth.HttpPrincipal;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import static org.easymock.EasyMock.*;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+
+public class GetUserActionTest
+{
+    @Test
+    public void writeUserXML() throws Exception
+    {
+        final HttpServletResponse mockResponse =
+                createMock(HttpServletResponse.class);
+        final UserPersistence<HttpPrincipal> mockUserPersistence =
+                createMock(UserPersistence.class);
+        final HttpPrincipal userID = new HttpPrincipal("CADCtest");
+
+        final GetUserAction testSubject = new GetUserAction(null, userID)
+        {
+            @Override
+            UserPersistence<HttpPrincipal> getUserPersistence()
+            {
+                return mockUserPersistence;
+            }
+        };
+
+        final User<HttpPrincipal> user = new User<HttpPrincipal>(userID);
+        final Writer writer = new StringWriter();
+        final PrintWriter printWriter = new PrintWriter(writer);
+
+        expect(mockUserPersistence.getUser(userID)).andReturn(user).once();
+        expect(mockResponse.getWriter()).andReturn(printWriter).once();
+        mockResponse.setContentType("text/xml");
+        expectLastCall().once();
+
+        replay(mockResponse, mockUserPersistence);
+
+        testSubject.doAction(null, mockResponse);
+
+        assertEquals("Wrong XML output.",
+                     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
+                     "<user>\r\n" +
+                     "  <userID>\r\n" +
+                     "    <identity type=\"HTTP\">CADCtest</identity>\r\n" +
+                     "  </userID>\r\n" +
+                     "</user>\r\n", writer.toString());
+        verify(mockResponse, mockUserPersistence);
+    }
+
+    @Test
+    public void writeUserJSON() throws Exception
+    {
+        final HttpServletResponse mockResponse =
+                createMock(HttpServletResponse.class);
+        final UserPersistence<HttpPrincipal> mockUserPersistence =
+                createMock(UserPersistence.class);
+        final HttpPrincipal userID = new HttpPrincipal("CADCtest");
+
+        final GetUserAction testSubject = new GetUserAction(null, userID)
+        {
+            @Override
+            UserPersistence<HttpPrincipal> getUserPersistence()
+            {
+                return mockUserPersistence;
+            }
+        };
+
+        testSubject.setAcceptedContentType(UsersAction.JSON_CONTENT_TYPE);
+
+        final User<HttpPrincipal> user = new User<HttpPrincipal>(userID);
+        final Writer writer = new StringWriter();
+        final PrintWriter printWriter = new PrintWriter(writer);
+
+        expect(mockUserPersistence.getUser(userID)).andReturn(user).once();
+        expect(mockResponse.getWriter()).andReturn(printWriter).once();
+        mockResponse.setContentType("application/json");
+        expectLastCall().once();
+
+        replay(mockResponse, mockUserPersistence);
+        testSubject.doAction(null, mockResponse);
+
+        assertEquals("Wrong JSON output.",
+                     "{\"user\":{\"userID\":{\"identity\":{\"name\":\"CADCtest\",\"type\":\"HTTP\"}}}}",
+                     writer.toString());
+        verify(mockResponse, mockUserPersistence);
+    }
+}
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java
index 67cba1aac80b91a28c96d039de47eded5cd802a0..9f68864b52147a94be6ea974d9ae7fd5cdfe178c 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java
@@ -70,18 +70,22 @@ package ca.nrc.cadc.ac.server.web.users;
 
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.ac.server.UserPersistence;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import ca.nrc.cadc.net.TransientException;
 import ca.nrc.cadc.util.Log4jInit;
 
 import java.io.*;
 import java.security.AccessControlException;
+import java.security.Principal;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.log4j.Level;
 import org.apache.log4j.Logger;
-import org.easymock.EasyMock;
+
+import static org.easymock.EasyMock.*;
 import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
 
 import static org.junit.Assert.*;
@@ -99,59 +103,6 @@ public class UsersActionTest
         Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO);
     }
 
-    @Test
-    public void writeUserXML() throws Exception
-    {
-        final UsersAction usersAction = new UsersAction(null)
-        {
-            @Override
-            public Object run() throws Exception
-            {
-                // Do nothing.
-                return null;
-            }
-        };
-
-        final User<HttpPrincipal> user =
-                new User<HttpPrincipal>(new HttpPrincipal("CADCtest"));
-        final Writer writer = new StringWriter();
-
-        usersAction.writeUser(user, writer);
-
-        assertEquals("Wrong XML output.",
-                     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
-                     "<user>\r\n" +
-                     "  <userID>\r\n" +
-                     "    <identity type=\"HTTP\">CADCtest</identity>\r\n" +
-                     "  </userID>\r\n" +
-                     "</user>\r\n", writer.toString());
-    }
-
-    @Test
-    public void writeUserJSON() throws Exception
-    {
-        final UsersAction usersAction = new UsersAction(null)
-        {
-            @Override
-            public Object run() throws Exception
-            {
-                // Do nothing.
-                return null;
-            }
-        };
-
-        final User<HttpPrincipal> user =
-                new User<HttpPrincipal>(new HttpPrincipal("CADCtest"));
-        final Writer writer = new StringWriter();
-
-        usersAction.setAcceptedContentType(UsersAction.JSON_CONTENT_TYPE);
-        usersAction.writeUser(user, writer);
-
-        assertEquals("Wrong JSON output.",
-                     "{\"user\":{\"userID\":{\"identity\":{\"name\":\"CADCtest\",\"type\":\"HTTP\"}}}}",
-                     writer.toString());
-    }
-
     @Test
     public void testDoActionAccessControlException() throws Exception
     {
@@ -191,69 +142,52 @@ public class UsersActionTest
     @Test
     public void testDoActionTransientException() throws Exception
     {
-        try
-        {
-            HttpServletResponse response = EasyMock
-                    .createMock(HttpServletResponse.class);
-            EasyMock.expect(response.isCommitted()).andReturn(Boolean.FALSE);
-            response.setContentType("text/plain");
-            EasyMock.expectLastCall().once();
-            EasyMock.expect(response.getWriter())
-                    .andReturn(new PrintWriter(new StringWriter()));
-            EasyMock.expectLastCall().once();
-            response.setStatus(503);
-            EasyMock.expectLastCall().once();
-            EasyMock.replay(response);
-
-            UserLogInfo logInfo = EasyMock.createMock(UserLogInfo.class);
-            logInfo.setSuccess(false);
-            EasyMock.expectLastCall().once();
-            logInfo.setMessage("Internal Transient Error: foo");
-            EasyMock.expectLastCall().once();
-            EasyMock.replay(logInfo);
-
-            UsersActionImpl action = new UsersActionImpl(logInfo);
-            action.setException(new TransientException("foo"));
-            action.doAction(null, response);
-        }
-        catch (Throwable t)
-        {
-            log.error(t.getMessage(), t);
-            fail("unexpected error: " + t.getMessage());
-        }
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        expect(response.isCommitted()).andReturn(Boolean.FALSE);
+        response.setContentType("text/plain");
+        expectLastCall().once();
+        expect(response.getWriter())
+                .andReturn(new PrintWriter(new StringWriter())).once();
+
+        response.setStatus(503);
+        expectLastCall().once();
+        replay(response);
+
+        UserLogInfo logInfo = createMock(UserLogInfo.class);
+        logInfo.setSuccess(false);
+        expectLastCall().once();
+        logInfo.setMessage("Internal Transient Error: foo");
+        expectLastCall().once();
+        replay(logInfo);
+
+        UsersActionImpl action = new UsersActionImpl(logInfo);
+        action.setException(new TransientException("foo"));
+        action.doAction(null, response);
     }
 
     private void testDoAction(String message, int responseCode, Exception e)
             throws Exception
     {
-        try
-        {
-            HttpServletResponse response = EasyMock
-                    .createMock(HttpServletResponse.class);
-            EasyMock.expect(response.isCommitted()).andReturn(Boolean.FALSE);
-            response.setContentType("text/plain");
-            EasyMock.expectLastCall().once();
-            EasyMock.expect(response.getWriter())
-                    .andReturn(new PrintWriter(new StringWriter()));
-            EasyMock.expectLastCall().once();
-            response.setStatus(responseCode);
-            EasyMock.expectLastCall().once();
-            EasyMock.replay(response);
-
-            UserLogInfo logInfo = EasyMock.createMock(UserLogInfo.class);
-            logInfo.setMessage(message);
-            EasyMock.expectLastCall().once();
-            EasyMock.replay(logInfo);
-
-            UsersActionImpl action = new UsersActionImpl(logInfo);
-            action.setException(e);
-            action.doAction(null, response);
-        }
-        catch (Throwable t)
-        {
-            log.error(t.getMessage(), t);
-            fail("unexpected error: " + t.getMessage());
-        }
+        HttpServletResponse response =
+                createMock(HttpServletResponse.class);
+        expect(response.isCommitted()).andReturn(Boolean.FALSE);
+        response.setContentType("text/plain");
+        expectLastCall().once();
+        expect(response.getWriter())
+                .andReturn(new PrintWriter(new StringWriter())).once();
+
+        response.setStatus(responseCode);
+        expectLastCall().once();
+        replay(response);
+
+        UserLogInfo logInfo = createMock(UserLogInfo.class);
+        logInfo.setMessage(message);
+        expectLastCall().once();
+        replay(logInfo);
+
+        UsersActionImpl action = new UsersActionImpl(logInfo);
+        action.setException(e);
+        action.doAction(null, response);
     }
 
     public class UsersActionImpl extends UsersAction
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java
index fea1d0be2502e40f5ae6d99d13f98ef1fb7d98ee..d1c28cfac115e291be92c9fc60618f32de33113f 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java
@@ -100,4 +100,27 @@ public class UserRequest<T extends Principal>
         return this.password;
     }
 
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o)
+        {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass())
+        {
+            return false;
+        }
+
+        UserRequest<?> that = (UserRequest<?>) o;
+
+        return user.equals(that.user);
+
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return user.hashCode();
+    }
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java
index d91932a3adebbb66544ed971821ef3a5819dbaa5..b6aedd7f92293b5f7c49580ff44fd5ef20078567 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java
@@ -91,8 +91,8 @@ public class UserReader
      * @throws ReaderException
      * @throws IOException
      */
-    public static User<? extends Principal> read(InputStream in)
-        throws ReaderException, IOException
+    public static User<Principal> read(InputStream in)
+        throws IOException
     {
         if (in == null)
         {
@@ -113,8 +113,8 @@ public class UserReader
      * @throws ReaderException
      * @throws IOException
      */
-    public static User<? extends Principal> read(Reader reader)
-        throws ReaderException, IOException
+    public static User<Principal> read(Reader reader)
+        throws IOException
     {
         if (reader == null)
         {
@@ -135,8 +135,8 @@ public class UserReader
      * @throws ReaderException
      * @throws IOException
      */
-    public static User<? extends Principal> read(String json)
-        throws ReaderException, IOException
+    public static User<Principal> read(String json)
+        throws IOException
     {
         if (json == null || json.isEmpty())
         {
@@ -156,7 +156,7 @@ public class UserReader
         }
     }
 
-    protected static User<? extends Principal> parseUser(JSONObject userObject)
+    protected static User<Principal> parseUser(JSONObject userObject)
         throws ReaderException, JSONException
     {
         JSONObject userIDObject = userObject.getJSONObject("userID");
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserRequestReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserRequestReader.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e0ed7a9d0095fa85fb07c6079c50b2b10dfb359
--- /dev/null
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserRequestReader.java
@@ -0,0 +1,168 @@
+/*
+ ************************************************************************
+ *******************  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.json;
+
+import ca.nrc.cadc.ac.ReaderException;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserRequest;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.*;
+import java.security.Principal;
+import java.util.Scanner;
+
+public class UserRequestReader
+{
+    /**
+     * Construct a UserRequest from an XML String source.
+     *
+     * @param json String of the XML.
+     * @return UserRequest UserRequest.
+     * @throws IOException
+     */
+    public static UserRequest<Principal> read(String json)
+        throws IOException
+    {
+        if (json == null)
+        {
+            throw new IllegalArgumentException("XML must not be null");
+        }
+        else
+        {
+            try
+            {
+                return parseUserRequest(new JSONObject(json));
+            }
+            catch (JSONException e)
+            {
+                String error = "Unable to parse JSON to User because " +
+                               e.getMessage();
+                throw new ReaderException(error, e);
+            }
+        }
+    }
+
+    /**
+     * Construct a User from a InputStream.
+     *
+     * @param in InputStream.
+     * @return User User.
+     * @throws ReaderException
+     * @throws IOException
+     */
+    public static UserRequest<Principal> read(InputStream in)
+            throws IOException
+    {
+        if (in == null)
+        {
+            throw new IOException("stream closed");
+        }
+
+        Scanner s = new Scanner(in).useDelimiter("\\A");
+        String json = s.hasNext() ? s.next() : "";
+
+        return read(json);
+    }
+
+    /**
+     * Construct a User from a Reader.
+     *
+     * @param reader Reader.
+     * @return User User.
+     * @throws ReaderException
+     * @throws IOException
+     */
+    public static UserRequest<Principal> read(Reader reader)
+            throws IOException
+    {
+        if (reader == null)
+        {
+            throw new IllegalArgumentException("reader must not be null");
+        }
+
+        Scanner s = new Scanner(reader).useDelimiter("\\A");
+        String json = s.hasNext() ? s.next() : "";
+
+        return read(json);
+    }
+
+
+    protected static UserRequest<Principal> parseUserRequest(
+            JSONObject userRequestObject)
+        throws ReaderException, JSONException
+    {
+        final User<Principal> user =
+                ca.nrc.cadc.ac.json.UserReader.parseUser(
+                        userRequestObject.getJSONObject("user"));
+
+        return new UserRequest<Principal>(user, userRequestObject.
+                getString("password"));
+    }
+}
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java
index 9b9ad2bbed498a5f1ca9f6b362db2b48a21559ab..e9b957c29ed85fcc8133c781a2b8618e15650116 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java
@@ -96,8 +96,8 @@ public class UserReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static User<? extends Principal> read(String xml)
-        throws ReaderException, IOException, URISyntaxException
+    public static User<Principal> read(String xml)
+        throws IOException, URISyntaxException
     {
         if (xml == null)
         {
@@ -111,12 +111,10 @@ public class UserReader
      * 
      * @param in InputStream.
      * @return User User.
-     * @throws ReaderException
      * @throws java.io.IOException
-     * @throws java.net.URISyntaxException
      */
-    public static User<? extends Principal> read(InputStream in)
-        throws ReaderException, IOException, URISyntaxException
+    public static User<Principal> read(InputStream in)
+        throws IOException
     {
         if (in == null)
         {
@@ -142,8 +140,8 @@ public class UserReader
      * @throws ReaderException
      * @throws java.io.IOException
      */
-    public static User<? extends Principal> read(Reader reader)
-        throws ReaderException, IOException
+    public static User<Principal> read(Reader reader)
+        throws IOException
     {
         if (reader == null)
         {
@@ -168,7 +166,7 @@ public class UserReader
         return parseUser(root);
     }
 
-    protected static User<? extends Principal> parseUser(Element userElement)
+    protected static User<Principal> parseUser(Element userElement)
         throws ReaderException
     {
         // userID element of the User element
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java
index f7c0e8553ab0bc61f3b6ff8d6f5827d4bd717605..da30cafda3aa6e952fa5eec3682318f9543b5555 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java
@@ -78,7 +78,6 @@ 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 org.jdom2.Document;
@@ -92,12 +91,10 @@ public class UserRequestReader
      *
      * @param xml String of the XML.
      * @return UserRequest UserRequest.
-     * @throws ReaderException
      * @throws java.io.IOException
-     * @throws java.net.URISyntaxException
      */
-    public static UserRequest<? extends Principal> read(String xml)
-        throws ReaderException, IOException, URISyntaxException
+    public static UserRequest<Principal> read(String xml)
+        throws IOException
     {
         if (xml == null)
         {
@@ -113,10 +110,9 @@ public class UserRequestReader
      * @return UserRequest UserRequest.
      * @throws ReaderException
      * @throws java.io.IOException
-     * @throws java.net.URISyntaxException
      */
-    public static UserRequest<? extends Principal> read(InputStream in)
-        throws ReaderException, IOException, URISyntaxException
+    public static UserRequest<Principal> read(InputStream in)
+        throws IOException
     {
         if (in == null)
         {
@@ -142,8 +138,8 @@ public class UserRequestReader
      * @throws ReaderException
      * @throws java.io.IOException
      */
-    public static UserRequest<? extends Principal> read(Reader reader)
-        throws ReaderException, IOException
+    public static UserRequest<Principal> read(Reader reader)
+        throws IOException
     {
         if (reader == null)
         {
@@ -168,7 +164,8 @@ public class UserRequestReader
         return parseUserRequest(root);
     }
 
-    protected static UserRequest parseUserRequest(Element userRequestElement)
+    protected static UserRequest<Principal> parseUserRequest(
+            Element userRequestElement)
         throws ReaderException
     {
         // user element of the UserRequest element
@@ -178,7 +175,7 @@ public class UserRequestReader
             String error = "user element not found in userRequest element";
             throw new ReaderException(error);
         }
-        User<? extends Principal> user = ca.nrc.cadc.ac.xml.UserReader.parseUser(userElement);
+        User<Principal> user = ca.nrc.cadc.ac.xml.UserReader.parseUser(userElement);
 
         // password element of the userRequest element
         Element passwordElement = userRequestElement.getChild("password");
@@ -189,9 +186,6 @@ public class UserRequestReader
         }
         String password = passwordElement.getText();
 
-        UserRequest userRequest = new UserRequest(user, password);
-
-        return userRequest;
+        return new UserRequest<Principal>(user, password);
     }
-
 }