diff --git a/projects/cadcAccessControl-Admin/build.xml b/projects/cadcAccessControl-Admin/build.xml
index 082c500c3755116f2e728b75ff241186b6f91916..69fca321f6832fdd7e56ab423a3c3b0051a8b49b 100644
--- a/projects/cadcAccessControl-Admin/build.xml
+++ b/projects/cadcAccessControl-Admin/build.xml
@@ -84,24 +84,69 @@
     <property name="project" value="userAdmin" />
 
     <!-- JAR files to be included in classpath and war file -->
-    <property name="cadcAccessControl"		value="${lib}/cadcAccessControl.jar" />
-    <property name="cadcAccessControl-Server"	value="${lib}/cadcAccessControl-Server.jar" />
-    <property name="cadcUtil"			value="${lib}/cadcUtil.jar" />
-    <property name="log4j"			value="${ext.lib}/log4j.jar" />
-    <property name="commons-logging"            value="${ext.lib}/commons-logging.jar" />
-    <property name="unboundid"                  value="${ext.lib}/unboundid-ldapsdk-se.jar" />
-    
-    <property name="cadc"       value="${cadcAccessControl}:${cadcAccessControl-Server}:${cadcUtil}" />
-    <property name="external"   value="${unboundid}:${log4j}" />
+    <property name="cadcAC"		        value="${lib}/cadcAccessControl.jar" />
+    <property name="cadcAC-Server"      value="${lib}/cadcAccessControl-Server.jar" />
+    <property name="cadcLog"		    value="${lib}/cadcLog.jar" />
+    <property name="cadcUtil"		    value="${lib}/cadcUtil.jar" />
+    <property name="log4j"			    value="${ext.lib}/log4j.jar" />
+    <property name="commons-logging"    value="${ext.lib}/commons-logging.jar" />
+    <property name="unboundid"          value="${ext.lib}/unboundid-ldapsdk-se.jar" />
+
+    <property name="jars" value="${cadcAC}:${cadcAC-Server}:${cadcUtil}:${log4j}" />
+
+    <property name="client.cadc.jars"       value="${cadcAC}:${cadcAC-Server}:${cadcLog}:${cadcUtil}" />
+    <property name="client.external.jars"   value="${unboundid}:${log4j}" />
 
     <property name="jars"       value="${cadc}:${external}" />
-    
-    <target name="build" depends="simpleJar" />
+
+    <target name="build" depends="compile,manifest">
+        <jar jarfile="${build}/lib/${project}.jar"
+             basedir="${build}/class" update="no"
+             manifest="${build}/tmp/${project}.mf">
+            <include name="ca/nrc/cadc/**" />
+        </jar>
+    </target>
+
+    <target name="setup-test" depends="init">
+        <copy overwrite="true"
+              file="${env.A}/test-certificates/x509_CADCAuthtest1.pem"
+              tofile="build/test/class/cadcauthtest1.pem"/>
+    </target>
+
+    <target name="manifest">
+        <pathconvert property="client.flat.manifest" pathsep=" ">
+            <mapper type="flatten"/>
+            <path> <pathelement path="${client.cadc.jars}"/> </path>
+            <path> <pathelement path="${client.external.jars}"/> </path>
+        </pathconvert>
+        <pathconvert property="client.non-flat.manifest" pathsep=" ">
+            <path> <pathelement path="${client.cadc.jars}"/> </path>
+            <path> <pathelement path="${client.external.jars}"/> </path>
+        </pathconvert>
+
+        <manifest file="${build}/tmp/${project}.mf" mode="replace">
+            <attribute name="Main-Class" value="ca.nrc.cadc.ac.admin.Main"/>
+            <attribute name="Class-Path" value="${client.flat.manifest} ${client.non-flat.manifest}"/>
+        </manifest>
+    </target>
 
     <!-- JAR files needed to run the test suite -->
+    <property name="cadcLog"          value="${lib}/cadcLog.jar" />
     <property name="junit"          value="${ext.dev}/junit.jar" />
 
-    <property name="testingJars" value="${junit}" />
-    
+    <property name="testingJars" value="${cadcLog}:${junit}:${unboundid}" />
 
+    <target name="single-test" depends="compile,compile-test,setup-test">
+        <echo message="Running test suite..." />
+        <junit printsummary="yes" haltonfailure="yes" fork="yes">
+            <classpath>
+                <pathelement path="${build}/class"/>
+                <pathelement path="${build}/test/class"/>
+                <pathelement path="${jars}:${testingJars}"/>
+            </classpath>
+            <sysproperty key="ca.nrc.cadc.util.PropertiesReader.dir" value="test"/>
+            <test name="ca.nrc.cadc.ac.admin.integration.AdminIntTest" />
+            <formatter type="plain" usefile="false" />
+        </junit>
+    </target>
 </project>
diff --git a/projects/cadcAccessControl-Admin/scripts/userAdmin b/projects/cadcAccessControl-Admin/scripts/userAdmin
index d766b8d9eba72b8f2038c7030f6d0ab5271f3b2b..d10c9488880347d1bd896a97ab347bc53a061206 100755
--- a/projects/cadcAccessControl-Admin/scripts/userAdmin
+++ b/projects/cadcAccessControl-Admin/scripts/userAdmin
@@ -1,5 +1,3 @@
-#!/bin/tcsh -f
+#!/bin/bash
 
-source ${DEFAULT_CONFIG_DIR}/JavaSystemProps.csh || exit $status
-
-java -Xmx128m ${JAVA_SYSTEM_PROPS} -jar ${CADC_ROOT}/lib/userAdmin.jar $*:q
+java -Xmx128m -jar ${CADC_ROOT}/lib/userAdmin.jar $*
diff --git a/projects/cadcAccessControl-Admin/src/ca/nrc/cadc/ac/admin/CmdLineParser.java b/projects/cadcAccessControl-Admin/src/ca/nrc/cadc/ac/admin/CmdLineParser.java
index a77895503a47088ca113251ff2761a53b7997268..f5c47774872e53cf4fee4a338b625822caea5fbb 100644
--- a/projects/cadcAccessControl-Admin/src/ca/nrc/cadc/ac/admin/CmdLineParser.java
+++ b/projects/cadcAccessControl-Admin/src/ca/nrc/cadc/ac/admin/CmdLineParser.java
@@ -207,6 +207,7 @@ public class CmdLineParser
         // only one command is allowed per command line
     	if (am.isSet("list"))
     	{
+            System.out.println("--list");
             this.command = new ListActiveUsers();
             count++;
     	}
@@ -260,7 +261,7 @@ public class CmdLineParser
     		
             if (count == 0)
             {
-                msg = "Missing command or ommand is not supported.";
+                msg = "Missing command or command is not supported.";
             }
             else
             {
@@ -289,6 +290,7 @@ public class CmdLineParser
             try 
             {
                 SSLUtil.validateSubject(subject, null);
+                log.debug("subject: " + subject);
                 this.subject = subject;
                 this.proceed = true;
             } 
@@ -323,11 +325,10 @@ public class CmdLineParser
     	sb.append("--list               :list users in the Users tree\n");
     	sb.append("                     :can be executed as an anonymous user\n");
     	sb.append("--list-pending       :list users in the UserRequests tree\n");
-    	sb.append("                     :except those with nsaccountlock=true\n");
     	sb.append("                     :can be executed as an anonymous user\n");
     	sb.append("--view=<userid>      :print the entire details of the user\n");
     	sb.append("--approve=<userid>   :delete the user from the UserRequests tree\n");
-    	sb.append("                     :by setting nsaccount=true, and insert it to the Users tree\n");
+    	sb.append("                     :and insert it into the Users tree\n");
     	sb.append("--reject=<userid>    :delete the user from the UserRequests tree\n");
     	sb.append("\n");
     	sb.append("-v|--verbose         : Verbose mode print progress and error messages\n");
diff --git a/projects/cadcAccessControl-Admin/test/LdapConfig.properties b/projects/cadcAccessControl-Admin/test/LdapConfig.properties
new file mode 100644
index 0000000000000000000000000000000000000000..57fdb97105d8df8f1a4fb538f734a7045feadbfb
--- /dev/null
+++ b/projects/cadcAccessControl-Admin/test/LdapConfig.properties
@@ -0,0 +1,8 @@
+# This are the configuration fields required by the unit tests
+server = proc5-03.cadc.dao.nrc.ca
+port = 389
+proxyUser = testproxy
+usersDn = ou=Users,ou=ds,dc=testcanfar
+userRequestsDN = ou=UserRequests,ou=ds,dc=testcanfar
+groupsDn = ou=Groups,ou=ds,dc=testcanfar
+adminGroupsDn = ou=adminGroups,ou=ds,dc=testcanfar
\ No newline at end of file
diff --git a/projects/cadcAccessControl-Admin/test/scripts/userAdminTest b/projects/cadcAccessControl-Admin/test/scripts/userAdminTest
new file mode 100755
index 0000000000000000000000000000000000000000..d10c9488880347d1bd896a97ab347bc53a061206
--- /dev/null
+++ b/projects/cadcAccessControl-Admin/test/scripts/userAdminTest
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+java -Xmx128m -jar ${CADC_ROOT}/lib/userAdmin.jar $*
diff --git a/projects/cadcAccessControl-Admin/test/src/ca/nrc/cadc/ac/admin/integration/AdminIntTest.java b/projects/cadcAccessControl-Admin/test/src/ca/nrc/cadc/ac/admin/integration/AdminIntTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..76f7adacd9955a5cf0b432982bccc541c938e4b7
--- /dev/null
+++ b/projects/cadcAccessControl-Admin/test/src/ca/nrc/cadc/ac/admin/integration/AdminIntTest.java
@@ -0,0 +1,401 @@
+/*
+ ************************************************************************
+ *******************  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.admin.integration;
+
+import ca.nrc.cadc.ac.PersonalDetails;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserAlreadyExistsException;
+import ca.nrc.cadc.ac.UserRequest;
+import ca.nrc.cadc.ac.server.ldap.LdapConfig;
+import ca.nrc.cadc.ac.server.ldap.LdapUserDAO;
+import ca.nrc.cadc.auth.DNPrincipal;
+import ca.nrc.cadc.auth.HttpPrincipal;
+import ca.nrc.cadc.net.TransientException;
+import ca.nrc.cadc.util.Log4jInit;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.security.auth.Subject;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.Principal;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class AdminIntTest
+{
+    private static final Logger log = Logger.getLogger(AdminIntTest.class);
+
+    static final String CONFIG = LdapConfig.class.getSimpleName() + ".test.properties";
+    static final String EXEC_CMD = "./scripts/userAdmin";
+
+    static String servopsCert;
+    static LdapConfig config;
+
+    @BeforeClass
+    public static void setUpClass()
+        throws Exception
+    {
+        Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG);
+
+        servopsCert = System.getProperty("user.home") + "/.pub/proxy.pem";
+
+        config = LdapConfig.getLdapConfig(CONFIG);
+    }
+
+    @Test
+    public void listUsers() throws Exception
+    {
+//        String userID = "CadcAdmin-int-test-user-" + System.currentTimeMillis();
+//        boolean isPending = false;
+//        addUser(userID, isPending);
+
+        String[] args = new String[] { "--list" };
+
+        List<String> output = doTest(args, 0);
+
+        assertFalse("output is empty", output.isEmpty());
+
+//        boolean found = false;
+//        for (String line : output)
+//        {
+//            if (line.equals(userID))
+//            {
+//                found = true;
+//            }
+//        }
+//        assertTrue("User not found", found);
+    }
+
+//    @Test
+    public void listPendingUsers() throws Exception
+    {
+        String userID = "CadcAdmin-int-test-user-" + System.currentTimeMillis();
+        boolean isPending = true;
+        addUser(userID, isPending);
+
+        String[] args = new String[] { "--list-pending" };
+
+        List<String> output = doTest(args, 0);
+
+        assertFalse("output is empty", output.isEmpty());
+
+        boolean found = false;
+        for (String line : output)
+        {
+            if (line.equals(userID))
+            {
+                found = true;
+            }
+        }
+        assertTrue("User not found", found);
+    }
+
+//    @Test
+    public void viewPendingUser() throws Exception
+    {
+        String userID = "CadcAdmin-int-test-user-" + System.currentTimeMillis();
+        boolean isPending = true;
+        addUser(userID, isPending);
+
+        String[] args = new String[] { "--view=" + userID };
+
+        List<String> output = doTest(args, 0);
+
+        assertFalse("output is empty", output.isEmpty());
+
+        boolean found = false;
+        for (String line : output)
+        {
+            if (line.contains(userID))
+            {
+                found = true;
+            }
+        }
+        assertTrue("User not found", found);
+    }
+
+//    @Test
+    public void viewPendingUserNotFound() throws Exception
+    {
+        String userID = "foo-" + System.currentTimeMillis();
+
+        String[] args = new String[] { "--view=" + userID };
+
+        List<String> output = doTest(args, 1);
+
+        assertTrue("output is empty", output.isEmpty());
+    }
+
+//    @Test
+    public void approvePendingUser() throws Exception
+    {
+        String userID = "CadcAdmin-int-test-user-" + System.currentTimeMillis();
+        boolean isPending = true;
+        addUser(userID, isPending);
+
+        String[] args = new String[] { "--approve=" + userID };
+
+        List<String> output = doTest(args, 0);
+
+        assertFalse("output is empty", output.isEmpty());
+
+        boolean found = false;
+        for (String line : output)
+        {
+            if (line.contains(userID))
+            {
+                found = true;
+            }
+        }
+        assertTrue("User not approved", found);
+
+        User<Principal> deletedUser = getUser(userID, true);
+        assertNull("User found", deletedUser);
+
+        User<Principal> approvedUser = getUser(userID, false);
+        assertNotNull("User not found", approvedUser);
+    }
+
+//    @Test
+    public void approvePendingUserNotFound() throws Exception
+    {
+        String userID = "foo-" + System.currentTimeMillis();
+
+        String[] args = new String[] { "--approve=" + userID };
+
+        List<String> output = doTest(args, 1);
+
+        assertTrue("output is empty", output.isEmpty());
+    }
+
+//    @Test
+    public void rejectPendingUser() throws Exception
+    {
+        String userID = "CadcAdmin-int-test-user-" + System.currentTimeMillis();
+        boolean isPending = true;
+        addUser(userID, isPending);
+
+        String[] args = new String[] { "--reject=" + userID };
+
+        List<String> output = doTest(args, 0);
+
+        assertFalse("output is empty", output.isEmpty());
+
+        boolean found = false;
+        for (String line : output)
+        {
+            if (line.contains(userID))
+            {
+                found = true;
+            }
+        }
+        assertTrue("User not rejected", found);
+
+        User<Principal> deletedUser = getUser(userID, isPending);
+        assertNull("User found", deletedUser);
+    }
+
+//    @Test
+    public void rejectPendingUserNotFound() throws Exception
+    {
+        String userID = "foo-" + System.currentTimeMillis();
+
+        String[] args = new String[] { "--reject=" + userID };
+
+        List<String> output = doTest(args, 1);
+
+        assertTrue("output is empty", output.isEmpty());
+    }
+
+    private List<String> doTest(String[] args, int expectedExitValue)
+        throws IOException, InterruptedException
+    {
+        String[] exec = new String[args.length + 2];
+        exec[0] = EXEC_CMD;
+        exec[1] = "--cert=" + servopsCert;
+
+        System.arraycopy(args, 0, exec, 2, args.length);
+        for (int i = 0; i < exec.length; i++)
+        {
+            log.debug("arg[" + i + "] = " + exec[i]);
+        }
+
+        ProcessBuilder pb = new ProcessBuilder(exec);
+        Process process = pb.start();
+
+        int exitValue = process.waitFor();
+
+        BufferedReader reader = new BufferedReader(
+            new InputStreamReader(process.getInputStream()));
+        List<String> output = new ArrayList<String>();
+        String line;
+        while ((line = reader.readLine()) != null)
+        {
+            output.add(line);
+        }
+
+        log.debug("Exit value: " + exitValue);
+        assertEquals("exit value", expectedExitValue, exitValue);
+        return output;
+    }
+
+    void addUser(final String userID, final boolean isPending)
+        throws UserAlreadyExistsException, TransientException
+    {
+        log.debug("adding " + userID + ", " + isPending);
+        HttpPrincipal httpPrincipal = new HttpPrincipal(userID);
+        final User<Principal> expected = new User<Principal>(httpPrincipal);
+        expected.getIdentities().add(httpPrincipal);
+
+        expected.details.add(new PersonalDetails("foo", "bar"));
+
+        final UserRequest<Principal> userRequest =
+            new UserRequest<Principal>(expected, "123456".toCharArray());
+
+        final LdapUserDAO<Principal> userDAO = getUserDAO();
+        if (isPending)
+        {
+            userDAO.addPendingUser(userRequest);
+        }
+        else
+        {
+            userDAO.addUser(userRequest);
+        }
+    }
+
+    User<Principal> getUser(final String username, final boolean isPending)
+        throws PrivilegedActionException
+    {
+        final HttpPrincipal userID = new HttpPrincipal(username);
+
+        Subject subject = new Subject();
+        subject.getPrincipals().add(userID);
+        subject.getPrincipals().add(getDNPrincipal(username, isPending));
+
+        PrivilegedExceptionAction<User<Principal>> action =
+            new PrivilegedExceptionAction<User<Principal>>()
+        {
+            public User<Principal> run()
+                throws Exception
+            {
+                try
+                {
+                    final LdapUserDAO<Principal> userDAO = getUserDAO();
+                    if (isPending)
+                    {
+                        return userDAO.getPendingUser(userID);
+                    }
+                    else
+                    {
+                        return userDAO.getUser(userID);
+                    }
+                }
+                catch (Exception e)
+                {
+                    throw new Exception("Problems", e);
+                }
+            }
+        };
+
+        return Subject.doAs(subject, action);
+    }
+
+    <T extends Principal> LdapUserDAO<T> getUserDAO()
+    {
+        return new LdapUserDAO(config);
+    }
+
+    DNPrincipal getDNPrincipal(final String username, final boolean isPending)
+    {
+        String entryDN = "uid=" + username + ",";
+        if (isPending)
+        {
+            entryDN = entryDN + config.getUserRequestsDN();
+        }
+        else
+        {
+            entryDN = entryDN + config.getUsersDN();
+        }
+
+        return new DNPrincipal(entryDN);
+    }
+
+}