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); + } + +}