diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml index 6799d81d742f9b12258fb3fe5266fba621e2b928..f30a6185a9e20cee5cc39941fc1f563c830e3410 100644 --- a/projects/cadcAccessControl-Server/build.xml +++ b/projects/cadcAccessControl-Server/build.xml @@ -69,7 +69,7 @@ <!DOCTYPE project> -<project default="build" basedir="."> +<project name="cadcAccessControl-Server" default="build" basedir="."> <property environment="env"/> <property file="local.build.properties" /> @@ -93,14 +93,16 @@ <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="${cadcAccessControl}:${cadcLog}:${cadcRegistry}:${cadcUtil}:${cadcUWS}:${jdom2}:${log4j}:${servlet}:${unboundid}:${xerces}" /> + <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" @@ -129,18 +131,18 @@ </fileset> </copy> </target> - -<!-- <target name="test" depends="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.LdapDAOTest" /> - <formatter type="plain" usefile="false" /> - </junit> - </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.LdapGroupDAOTest" />--> + <!--<formatter type="plain" usefile="false" />--> + <!--</junit>--> + <!--</target>--> </project> diff --git a/projects/cadcAccessControl-Server/config/.dbrc_example b/projects/cadcAccessControl-Server/config/.dbrc_example new file mode 100644 index 0000000000000000000000000000000000000000..ced094848f172ce8628940a22568afa4efb3d6be --- /dev/null +++ b/projects/cadcAccessControl-Server/config/.dbrc_example @@ -0,0 +1,2 @@ +#server proxyuser proxyUserDN password driver serverURL +<server hostname> <proxyUser in LdapConfig.properties> <proxyUserLdapDN> <password> N/A N/A diff --git a/projects/cadcAccessControl-Server/config/LdapConfig.properties b/projects/cadcAccessControl-Server/config/LdapConfig.properties index af73b275d60d77e64b1482aaa4a89f0f6450409d..5eb874d802b890852e308e6880946751513437dc 100644 --- a/projects/cadcAccessControl-Server/config/LdapConfig.properties +++ b/projects/cadcAccessControl-Server/config/LdapConfig.properties @@ -1,9 +1,7 @@ -# This are the configuration fields required by the Ldap ldap-dao unit tests -server = mach275.cadc.dao.nrc.ca -port = 389 -admin = uid=webproxy,ou=administrators,ou=topologymanagement,o=netscaperoot -passwd = go4it -usersDn = ou=Users,ou=ds,dc=canfar,dc=net -groupsDn = ou=Groups,ou=ds,dc=canfar,dc=net -deletedGroupsDN = ou=DeletedGroups,ou=ds,dc=canfar,dc=net -testGroupsDN = ou=TestGroups,ou=ds,dc=canfar,dc=net \ No newline at end of file +# This are the configuration fields required by the Ldap +server = <name of server> +port = <389 or 636> +proxyUser = <name of proxy user> +usersDn = <DN of users branch> +groupsDn = <DN of groups branch> +adminGroupsDn = <DN of admin groups> diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java index caf9dc42cabef2c2ddb1cbf3af504742b05a9706..bdfa4e05c79516396085e5ffb90d031dc2d7c3e2 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java @@ -81,6 +81,16 @@ import ca.nrc.cadc.net.TransientException; public abstract interface GroupPersistence<T extends Principal> { + /** + * Get all group names. + * + * @return A collection of strings. + * @throws TransientException If an temporary, unexpected problem occurred. + * @throws AccessControlException If the operation is not permitted. + */ + public Collection<String> getGroupNames() + throws TransientException, AccessControlException; + /** * Get the group with the given Group ID. * @@ -108,10 +118,13 @@ public abstract interface GroupPersistence<T extends Principal> * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. * @throws UserNotFoundException If owner or a member not valid user. + * @throws GroupNotFoundException if one of the groups in group members or + * group admins does not exist in the server. */ public abstract Group addGroup(Group group) throws GroupAlreadyExistsException, TransientException, - AccessControlException, UserNotFoundException; + AccessControlException, UserNotFoundException, + GroupNotFoundException; /** * Deletes the group. diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java index 17bd814f4431099e7c30f708ea4770c19e03f31f..42995612395ededc619d730d5a1441c31aea0900 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapConfig.java @@ -68,14 +68,24 @@ */ package ca.nrc.cadc.ac.server.ldap; +import java.io.FileNotFoundException; import java.io.IOException; -import java.net.URL; -import java.util.Properties; +import java.util.List; import org.apache.log4j.Logger; +import ca.nrc.cadc.db.ConnectionConfig; +import ca.nrc.cadc.db.DBConfig; +import ca.nrc.cadc.util.MultiValuedProperties; +import ca.nrc.cadc.util.PropertiesReader; import ca.nrc.cadc.util.StringUtil; +/** + * Reads and stores the LDAP configuration information. The information + * + * @author adriand + * + */ public class LdapConfig { private static final Logger logger = Logger.getLogger(LdapConfig.class); @@ -84,125 +94,122 @@ public class LdapConfig ".properties"; public static final String LDAP_SERVER = "server"; public static final String LDAP_PORT = "port"; - public static final String LDAP_ADMIN = "admin"; - public static final String LDAP_PASSWD = "passwd"; + public static final String LDAP_SERVER_PROXY_USER = "proxyUser"; public static final String LDAP_USERS_DN = "usersDn"; public static final String LDAP_GROUPS_DN = "groupsDn"; public static final String LDAP_ADMIN_GROUPS_DN = "adminGroupsDn"; - - public static final String LDAP_AVAIL_TEST_GROUP = "availabilityTestGroup"; - public static final String LDAP_AVAIL_TEST_CALLING_USER_DN = "availabilityTestCallingUserDN"; + + private final static int SECURE_PORT = 636; private String usersDN; private String groupsDN; private String adminGroupsDN; private String server; private int port; - private String adminUserDN; - private String adminPasswd; + private String proxyUserDN; + private String proxyPasswd; - private String availabilityTestGroup; - private String availabilityTestCallingUserDN; + public String getProxyUserDN() + { + return proxyUserDN; + } + + public String getProxyPasswd() + { + return proxyPasswd; + } public static LdapConfig getLdapConfig() { - Properties config = new Properties(); - URL url = null; - try - { - url = LdapConfig.class.getClassLoader().getResource(CONFIG); - logger.debug("Using config from: " + url); - if (url != null) - { - config.load(url.openStream()); - } - else - { - throw new IOException("File not found"); - } - } - catch (Exception ex) + return getLdapConfig(CONFIG); + } + + public static LdapConfig getLdapConfig(final String ldapProperties) + { + PropertiesReader pr = new PropertiesReader(ldapProperties); + + MultiValuedProperties config = pr.getAllProperties(); + + if (config.keySet() == null) { - throw new RuntimeException("failed to read " + CONFIG + - " from " + url, ex); + throw new RuntimeException("failed to read any LDAP property "); } - - String server = config.getProperty(LDAP_SERVER); - if (!StringUtil.hasText(server)) + + List<String> prop = config.getProperty(LDAP_SERVER); + if ((prop == null) || (prop.size() != 1)) { throw new RuntimeException("failed to read property " + LDAP_SERVER); } + String server = prop.get(0); - String port = config.getProperty(LDAP_PORT); - if (!StringUtil.hasText(port)) + prop = config.getProperty(LDAP_PORT); + if ((prop == null) || (prop.size() != 1)) { throw new RuntimeException("failed to read property " + LDAP_PORT); } - - String ldapAdmin = config.getProperty(LDAP_ADMIN); - if (!StringUtil.hasText(ldapAdmin)) - { - throw new RuntimeException("failed to read property " + LDAP_ADMIN); - } - - String ldapPasswd = config.getProperty(LDAP_PASSWD); - if (!StringUtil.hasText(ldapPasswd)) + int port = Integer.valueOf(prop.get(0)); + + prop = config.getProperty(LDAP_SERVER_PROXY_USER); + if ((prop == null) || (prop.size() != 1)) { throw new RuntimeException("failed to read property " + - LDAP_PASSWD); + LDAP_SERVER_PROXY_USER); } - - String ldapUsersDn = config.getProperty(LDAP_USERS_DN); - if (!StringUtil.hasText(ldapUsersDn)) + String ldapProxy = prop.get(0); + + prop = config.getProperty(LDAP_USERS_DN); + if ((prop == null) || (prop.size() != 1)) { throw new RuntimeException("failed to read property " + LDAP_USERS_DN); } + String ldapUsersDn = prop.get(0); - String ldapGroupsDn = config.getProperty(LDAP_GROUPS_DN); - if (!StringUtil.hasText(ldapGroupsDn)) + prop = config.getProperty(LDAP_GROUPS_DN); + if ((prop == null) || (prop.size() != 1)) { throw new RuntimeException("failed to read property " + LDAP_GROUPS_DN); } + String ldapGroupsDn = prop.get(0); - String ldapAdminGroupsDn = config.getProperty(LDAP_ADMIN_GROUPS_DN); - if (!StringUtil.hasText(ldapAdminGroupsDn)) + prop = config.getProperty(LDAP_ADMIN_GROUPS_DN); + if ((prop == null) || (prop.size() != 1)) { throw new RuntimeException("failed to read property " + LDAP_ADMIN_GROUPS_DN); } + String ldapAdminGroupsDn = prop.get(0); - String availGroup = config.getProperty(LDAP_AVAIL_TEST_GROUP); - if (!StringUtil.hasText(availGroup)) + DBConfig dbConfig; + try { - throw new RuntimeException("failed to read property " + - LDAP_AVAIL_TEST_GROUP); + dbConfig = new DBConfig(); + } + catch (FileNotFoundException e) + { + throw new RuntimeException("failed to find .dbrc file "); + } + catch (IOException e) + { + throw new RuntimeException("failed to read .dbrc file "); } - - String availUser = config.getProperty(LDAP_AVAIL_TEST_CALLING_USER_DN); - if (!StringUtil.hasText(availUser)) + ConnectionConfig cc = dbConfig.getConnectionConfig(server, ldapProxy); + if ( (cc == null) || (cc.getUsername() == null) || (cc.getPassword() == null)) { - throw new RuntimeException("failed to read property " + - LDAP_AVAIL_TEST_CALLING_USER_DN); + throw new RuntimeException("failed to find connection info in ~/.dbrc"); } - - return new LdapConfig(server, Integer.valueOf(port), ldapAdmin, - ldapPasswd, ldapUsersDn, ldapGroupsDn, - ldapAdminGroupsDn, availGroup, availUser); + + return new LdapConfig(server, Integer.valueOf(port), cc.getUsername(), + cc.getPassword(), ldapUsersDn, ldapGroupsDn, + ldapAdminGroupsDn); } - public LdapConfig(String server, int port, String adminUserDN, - String adminPasswd, String usersDN, String groupsDN, - String adminGroupsDN) - { - this(server, port, adminUserDN, adminPasswd, usersDN, groupsDN, adminGroupsDN, null, null); - } - public LdapConfig(String server, int port, String adminUserDN, - String adminPasswd, String usersDN, String groupsDN, - String adminGroupsDN, String availGroup, String availUser) + public LdapConfig(String server, int port, String proxyUserDN, + String proxyPasswd, String usersDN, String groupsDN, + String adminGroupsDN) { if (!StringUtil.hasText(server)) { @@ -213,11 +220,11 @@ public class LdapConfig throw new IllegalArgumentException("Illegal LDAP server port: " + port); } - if (!StringUtil.hasText(adminUserDN)) + if (!StringUtil.hasText(proxyUserDN)) { throw new IllegalArgumentException("Illegal Admin DN"); } - if (!StringUtil.hasText(adminPasswd)) + if (!StringUtil.hasText(proxyPasswd)) { throw new IllegalArgumentException("Illegal Admin password"); } @@ -234,16 +241,14 @@ public class LdapConfig throw new IllegalArgumentException("Illegal admin groups LDAP DN"); } - this.server = server; this.port = port; - this.adminUserDN = adminUserDN; - this.adminPasswd = adminPasswd; + this.proxyUserDN = proxyUserDN; + this.proxyPasswd = proxyPasswd; this.usersDN = usersDN; this.groupsDN = groupsDN; this.adminGroupsDN = adminGroupsDN; - this.availabilityTestGroup = availGroup; - this.availabilityTestCallingUserDN = availUser; + logger.debug(toString()); } public String getUsersDN() @@ -271,24 +276,32 @@ public class LdapConfig return this.port; } - public String getAdminUserDN() + public boolean isSecure() { - return this.adminUserDN; + return getPort() == SECURE_PORT; } - public String getAdminPasswd() + public String getAdminUserDN() { - return this.adminPasswd; + return this.proxyUserDN; } - - public String getAvailabilityTestGroup() + + public String getAdminPasswd() { - return this.availabilityTestGroup; + return this.proxyPasswd; } - - public String getAvailabilityTestCallingUserDN() + + public String toString() { - return this.availabilityTestCallingUserDN; + StringBuilder sb = new StringBuilder(); + sb.append("server = "); + sb.append(server); + sb.append(" port = "); + sb.append(port); + sb.append(" proxyUserDN = "); + sb.append(proxyUserDN); + sb.append(" proxyPasswd = "); + sb.append(proxyPasswd); + return sb.toString(); } - } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java index c33961f36a8119f28f915b2405a4375127044558..abd19b24bde0eda15142be362587d4b22241b26a 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapDAO.java @@ -68,31 +68,28 @@ */ package ca.nrc.cadc.ac.server.ldap; -import java.security.AccessControlException; -import java.security.AccessController; -import java.security.Principal; -import java.util.Set; - +import javax.net.SocketFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; +import java.io.File; +import java.net.MalformedURLException; +import java.security.*; +import java.security.cert.CertificateException; +import java.util.Set; + +import com.unboundid.ldap.sdk.*; +import com.unboundid.util.ssl.*; -import ca.nrc.cadc.auth.HttpPrincipal; -import ca.nrc.cadc.auth.NumericPrincipal; -import ca.nrc.cadc.auth.OpenIdPrincipal; +import ca.nrc.cadc.auth.*; import ca.nrc.cadc.net.TransientException; -import com.unboundid.ldap.sdk.DN; -import com.unboundid.ldap.sdk.LDAPConnection; -import com.unboundid.ldap.sdk.LDAPException; -import com.unboundid.ldap.sdk.ResultCode; -import com.unboundid.ldap.sdk.SearchResult; -import com.unboundid.ldap.sdk.SearchResultEntry; -import com.unboundid.ldap.sdk.SearchScope; public abstract class LdapDAO { private LDAPConnection conn; - + LdapConfig config; DN subjDN = null; @@ -114,34 +111,64 @@ public abstract class LdapDAO } protected LDAPConnection getConnection() - throws LDAPException, AccessControlException + throws LDAPException, AccessControlException { if (conn == null) { - conn = new LDAPConnection(config.getServer(), config.getPort()); + conn = new LDAPConnection(getSocketFactory(), config.getServer(), + config.getPort()); conn.bind(config.getAdminUserDN(), config.getAdminPasswd()); } return conn; } + private SocketFactory getSocketFactory() + { + final SocketFactory socketFactory; + + if (config.isSecure()) + { + socketFactory = createSSLSocketFactory(); + } + else + { + socketFactory = SocketFactory.getDefault(); + } + + return socketFactory; + } + + private SSLSocketFactory createSSLSocketFactory() + { + try + { + return new com.unboundid.util.ssl.SSLUtil(). + createSSLSocketFactory(); + } + catch (GeneralSecurityException e) + { + throw new RuntimeException("Unexpected error.", e); + } + } + protected DN getSubjectDN() throws LDAPException { if (subjDN == null) { - Subject callerSubject = + Subject callerSubject = Subject.getSubject(AccessController.getContext()); if (callerSubject == null) { throw new AccessControlException("Caller not authenticated."); } - + Set<Principal> principals = callerSubject.getPrincipals(); if (principals.isEmpty()) { throw new AccessControlException("Caller not authenticated."); } - + String ldapField = null; for (Principal p : principals) { @@ -172,31 +199,31 @@ public abstract class LdapDAO throw new AccessControlException("Identity of caller unknown."); } - SearchResult searchResult = - getConnection().search(config.getUsersDN(), SearchScope.ONE, - ldapField, new String[] {"entrydn"}); + SearchResult searchResult = + getConnection().search(config.getUsersDN(), SearchScope.ONE, + ldapField, "entrydn"); if (searchResult.getEntryCount() < 1) { throw new AccessControlException( "No LDAP account when search with rule " + ldapField); } - - subjDN = ((SearchResultEntry) searchResult.getSearchEntries() - .get(0)).getAttributeValueAsDN("entrydn"); + + subjDN = (searchResult.getSearchEntries().get(0)) + .getAttributeValueAsDN("entrydn"); } return subjDN; } - + /** * Checks the Ldap result code, and if the result is not SUCCESS, - * throws an appropriate exception. This is the place to decide on + * throws an appropriate exception. This is the place to decide on * mapping between ldap errors and exception types - * @param code - * @param errorMsg - * @throws TransientException + * + * @param code The code returned from an LDAP request. + * @throws TransientException */ - protected static void checkLdapResult(ResultCode code) + protected static void checkLdapResult(ResultCode code) throws TransientException { if (code == ResultCode.INSUFFICIENT_ACCESS_RIGHTS) @@ -207,7 +234,8 @@ public abstract class LdapDAO { throw new AccessControlException("Invalid credentials "); } - else if ((code == ResultCode.SUCCESS) || (code == ResultCode.NO_SUCH_OBJECT) ) + else if ((code == ResultCode.SUCCESS) || (code + == ResultCode.NO_SUCH_OBJECT)) { // all good. nothing to do } @@ -216,7 +244,7 @@ public abstract class LdapDAO throw new IllegalArgumentException("Error in Ldap parameters "); } else if (code == ResultCode.BUSY || - code == ResultCode.CONNECT_ERROR ) + code == ResultCode.CONNECT_ERROR) { throw new TransientException("Connection problems "); } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java index b85a4a31addbde3ca5d12220f2e6de6a0569118f..70ee0595f868c4805fda36310ff6cbd9b9278a5b 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java @@ -88,6 +88,7 @@ import ca.nrc.cadc.ac.Role; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.net.TransientException; +import ca.nrc.cadc.util.StringUtil; import com.unboundid.ldap.sdk.AddRequest; import com.unboundid.ldap.sdk.Attribute; @@ -134,10 +135,12 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO * exists. * @throws TransientException If an temporary, unexpected problem occurred. * @throws UserNotFoundException If owner or a member not valid user. + * @throws GroupNotFoundException */ public Group addGroup(final Group group) throws GroupAlreadyExistsException, TransientException, - UserNotFoundException, AccessControlException + UserNotFoundException, AccessControlException, + GroupNotFoundException { if (group.getOwner() == null) { @@ -204,7 +207,8 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO final DN ownerDN, final String description, final Set<User<? extends Principal>> users, final Set<Group> groups) - throws UserNotFoundException, LDAPException, TransientException + throws UserNotFoundException, LDAPException, TransientException, + AccessControlException, GroupNotFoundException { // add new group List<Attribute> attributes = new ArrayList<Attribute>(); @@ -214,7 +218,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO attributes.add(new Attribute("objectClass", "groupofuniquenames")); attributes.add(new Attribute("cn", groupID)); - if (description != null) + if (StringUtil.hasText(description)) { attributes.add(new Attribute("description", description)); } @@ -227,7 +231,12 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } for (Group groupMember : groups) { - DN memberDN = getGroupDN(groupMember.getID()); + final String groupMemberID = groupMember.getID(); + if (!checkGroupExists(groupMemberID)) + { + throw new GroupNotFoundException(groupMemberID); + } + DN memberDN = getGroupDN(groupMemberID); members.add(memberDN.toNormalizedString()); } if (!members.isEmpty()) @@ -305,6 +314,60 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new RuntimeException("Unexpected LDAP exception", e); } } + + + /** + * Get all group names. + * + * @return A collection of strings + * + * @throws TransientException If an temporary, unexpected problem occurred. + */ + public Collection<String> getGroupNames() + throws TransientException + { + try + { + Filter filter = Filter.createPresenceFilter("cn"); + String [] attributes = new String[] {"cn", "nsaccountlock"}; + + SearchRequest searchRequest = + new SearchRequest(config.getGroupsDN(), + SearchScope.SUB, filter, attributes); + + SearchResult searchResult = null; + try + { + searchResult = getConnection().search(searchRequest); + } + catch (LDAPSearchException e) + { + if (e.getResultCode() == ResultCode.NO_SUCH_OBJECT) + { + logger.debug("Could not find groups root", e); + throw new IllegalStateException("Could not find groups root"); + } + } + + LdapDAO.checkLdapResult(searchResult.getResultCode()); + List<String> groupNames = new ArrayList<String>(); + for (SearchResultEntry next : searchResult.getSearchEntries()) + { + if (!next.hasAttribute("nsaccountlock")) + { + groupNames.add(next.getAttributeValue("cn")); + } + } + + return groupNames; + } + catch (LDAPException e1) + { + LdapDAO.checkLdapResult(e1.getResultCode()); + throw new IllegalStateException("Unexpected exception: " + e1.getMatchedDN(), e1); + } + + } /** * Get the group with the given Group ID. @@ -418,8 +481,12 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new GroupNotFoundException(groupID); } - String groupCN = searchEntry.getAttributeValue("cn"); DN groupOwner = searchEntry.getAttributeValueAsDN("owner"); + if (groupOwner == null) + { + //TODO assume user not allowed to read group + throw new AccessControlException(groupID); + } User<X500Principal> owner; try @@ -431,7 +498,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new RuntimeException("BUG: group owner not found"); } - Group ldapGroup = new Group(groupCN, owner); + Group ldapGroup = new Group(groupID, owner); if (searchEntry.hasAttribute("description")) { ldapGroup.description = @@ -544,8 +611,12 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } for (Group gr : group.getGroupMembers()) { - DN grDN = getGroupDN(gr.getID()); - newMembers.add(grDN.toNormalizedString()); + if (!checkGroupExists(gr.getID())) + { + throw new GroupNotFoundException(gr.getID()); + } + DN grDN = getGroupDN(gr.getID()); + newMembers.add(grDN.toNormalizedString()); } List<String> newAdmins = new ArrayList<String>(); for (User<?> member : group.getUserAdmins()) @@ -555,8 +626,12 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } for (Group gr : group.getGroupAdmins()) { + if (!checkGroupExists(gr.getID())) + { + throw new GroupNotFoundException(gr.getID()); + } DN grDN = getGroupDN(gr.getID()); - newMembers.add(grDN.toNormalizedString()); + newAdmins.add(grDN.toNormalizedString()); } mods.add(new Modification(ModificationType.REPLACE, "uniquemember", @@ -691,7 +766,16 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO GroupNotFoundException, UserNotFoundException { User<T> user = new User<T>(userID); - DN userDN = userPersist.getUserDN(user); + DN userDN = null; + try + { + userDN = userPersist.getUserDN(user); + } + catch (UserNotFoundException e) + { + // no anonymous searches + throw new AccessControlException("Not authorized to search"); + } Collection<DN> groupDNs = new HashSet<DN>(); if (role == Role.OWNER) @@ -731,9 +815,10 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO } catch (GroupNotFoundException e) { - throw new IllegalStateException( - "BUG: group " + groupDN + " not found but " + - "membership exists (" + userID + ")"); + final String message = "BUG: group " + groupDN + " not found but " + + "membership exists (" + userID + ")"; + logger.error(message); + //throw new IllegalStateException(message); } } } @@ -931,5 +1016,18 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO throw new RuntimeException(e); } } + + private boolean checkGroupExists(String groupID) + throws TransientException + { + for (String groupName : getGroupNames()) + { + if (groupName.equalsIgnoreCase(groupID)) + { + return true; + } + } + return false; + } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java index 4ef7ef53f58a5bcfa7346d972499ce736633e0fa..f59bc1518d82c3522bf0cd104bc3ca8fecfc7ebf 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java @@ -93,7 +93,32 @@ public class LdapGroupPersistence<T extends Principal> { config = LdapConfig.getLdapConfig(); } - + + public Collection<String> getGroupNames() + throws TransientException, AccessControlException + { + LdapGroupDAO<T> groupDAO = null; + LdapUserDAO<T> userDAO = null; + try + { + userDAO = new LdapUserDAO<T>(config); + groupDAO = new LdapGroupDAO<T>(config, userDAO); + Collection<String> ret = groupDAO.getGroupNames(); + return ret; + } + finally + { + if (groupDAO != null) + { + groupDAO.close(); + } + if (userDAO != null) + { + userDAO.close(); + } + } + } + public Group getGroup(String groupName) throws GroupNotFoundException, TransientException, AccessControlException @@ -122,7 +147,8 @@ public class LdapGroupPersistence<T extends Principal> public Group addGroup(Group group) throws GroupAlreadyExistsException, TransientException, - AccessControlException, UserNotFoundException + AccessControlException, UserNotFoundException, + GroupNotFoundException { LdapGroupDAO<T> groupDAO = null; LdapUserDAO<T> userDAO = null; diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java index c3e33dca2cfe6a99fd87095489074f8ff234f804..f929385511697284380d6e5f6f4abef2d6457ce9 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java @@ -68,6 +68,7 @@ */ package ca.nrc.cadc.ac.server.ldap; +import javax.security.auth.x500.X500Principal; import java.security.AccessControlException; import java.security.Principal; import java.util.Collection; @@ -75,32 +76,25 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import javax.security.auth.x500.X500Principal; - +import com.unboundid.ldap.sdk.*; +import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; import org.apache.log4j.Logger; import ca.nrc.cadc.ac.PersonalDetails; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserNotFoundException; -import ca.nrc.cadc.auth.AuthenticationUtil; import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.net.TransientException; -import com.unboundid.ldap.sdk.DN; -import com.unboundid.ldap.sdk.Filter; -import com.unboundid.ldap.sdk.LDAPException; -import com.unboundid.ldap.sdk.SearchRequest; -import com.unboundid.ldap.sdk.SearchResultEntry; -import com.unboundid.ldap.sdk.SearchScope; -import com.unboundid.ldap.sdk.controls.ProxiedAuthorizationV2RequestControl; public class LdapUserDAO<T extends Principal> extends LdapDAO { private static final Logger logger = Logger.getLogger(LdapUserDAO.class); // Map of identity type to LDAP attribute - private Map<Class<?>, String> userLdapAttrib = new HashMap<Class<?>, String>(); - + private Map<Class<?>, String> userLdapAttrib = + new HashMap<Class<?>, String>(); + // User attributes returned to the GMS private static final String LDAP_FNAME = "givenname"; private static final String LDAP_LNAME = "sn"; @@ -113,54 +107,60 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO super(config); this.userLdapAttrib.put(HttpPrincipal.class, "uid"); this.userLdapAttrib.put(X500Principal.class, "distinguishedname"); - + // add the id attributes to user and member attributes - String[] princs = userLdapAttrib.values().toArray(new String[userLdapAttrib.values().size()]); + String[] princs = userLdapAttrib.values() + .toArray(new String[userLdapAttrib.values().size()]); String[] tmp = new String[userAttribs.length + princs.length]; System.arraycopy(princs, 0, tmp, 0, princs.length); - System.arraycopy(userAttribs, 0, tmp, princs.length, userAttribs.length); + System.arraycopy(userAttribs, 0, tmp, princs.length, + userAttribs.length); userAttribs = tmp; - + tmp = new String[memberAttribs.length + princs.length]; System.arraycopy(princs, 0, tmp, 0, princs.length); - System.arraycopy(memberAttribs, 0, tmp, princs.length, memberAttribs.length); + System.arraycopy(memberAttribs, 0, tmp, princs.length, + memberAttribs.length); memberAttribs = tmp; } - /** * Get the user specified by userID. * * @param userID The userID. - * * @return User instance. - * - * @throws UserNotFoundException when the user is not found. - * @throws TransientException If an temporary, unexpected problem occurred. + * @throws UserNotFoundException when the user is not found. + * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. */ public User<T> getUser(T userID) - throws UserNotFoundException, TransientException, AccessControlException + throws UserNotFoundException, TransientException, + AccessControlException { - String searchField = (String) userLdapAttrib.get(userID.getClass()); + String searchField = userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( "Unsupported principal type " + userID.getClass()); } - searchField = "(&(objectclass=cadcaccount)(" + searchField + "=" + userID.getName() + "))"; + searchField = + "(&(objectclass=cadcaccount)(" + searchField + "=" + userID + .getName() + "))"; SearchResultEntry searchResult = null; try { - SearchRequest searchRequest = new SearchRequest(config.getUsersDN(), - SearchScope.SUB, searchField, userAttribs); - + SearchRequest searchRequest = new SearchRequest(config.getUsersDN(), + SearchScope.SUB, + searchField, + userAttribs); + searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); + new ProxiedAuthorizationV2RequestControl("dn:" + + getSubjectDN() + .toNormalizedString())); searchResult = getConnection().searchForEntry(searchRequest); } @@ -178,57 +178,57 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO User<T> user = new User<T>(userID); user.getIdentities().add( new HttpPrincipal(searchResult.getAttributeValue(userLdapAttrib - .get(HttpPrincipal.class)))); - + .get(HttpPrincipal.class)))); + String fname = searchResult.getAttributeValue(LDAP_FNAME); String lname = searchResult.getAttributeValue(LDAP_LNAME); user.details.add(new PersonalDetails(fname, lname)); //TODO populate user with the other returned personal or posix attributes return user; - } + } /** * Get all groups the user specified by userID belongs to. - * - * @param userID The userID. + * + * @param userID The userID. * @param isAdmin - * * @return Collection of Group instances. - * * @throws UserNotFoundException when the user is not found. - * @throws TransientException If an temporary, unexpected problem occurred., e.getMessage( + * @throws TransientException If an temporary, unexpected problem occurred., e.getMessage( * @throws AccessControlException If the operation is not permitted. */ public Collection<DN> getUserGroups(final T userID, final boolean isAdmin) - throws UserNotFoundException, TransientException, AccessControlException + throws UserNotFoundException, TransientException, + AccessControlException { Collection<DN> groupDNs = new HashSet<DN>(); try { - String searchField = (String) userLdapAttrib.get(userID.getClass()); + String searchField = userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( "Unsupported principal type " + userID.getClass()); } - User<T> user = getUser(userID); + User<T> user = getUser(userID); Filter filter = Filter.createANDFilter( - Filter.createEqualityFilter(searchField, - user.getUserID().getName()), - Filter.createPresenceFilter("memberOf")); + Filter.createEqualityFilter(searchField, + user.getUserID().getName()), + Filter.createPresenceFilter("memberOf")); - SearchRequest searchRequest = - new SearchRequest(config.getUsersDN(), SearchScope.SUB, + SearchRequest searchRequest = + new SearchRequest(config.getUsersDN(), SearchScope.SUB, filter, "memberOf"); searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); + new ProxiedAuthorizationV2RequestControl("dn:" + + getSubjectDN() + .toNormalizedString())); - SearchResultEntry searchResult = + SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest); - + DN parentDN; if (isAdmin) { @@ -238,7 +238,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO { parentDN = new DN(config.getGroupsDN()); } - + if (searchResult != null) { String[] members = searchResult.getAttributeValues("memberOf"); @@ -253,7 +253,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO } } } - } + } } catch (LDAPException e) { @@ -261,26 +261,24 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO } return groupDNs; } - + /** * Check whether the user is a member of the group. * - * @param userID The userID. + * @param userID The userID. * @param groupID The groupID. - * * @return true or false - * - * @throws UserNotFoundException If the user is not found. - * @throws TransientException If an temporary, unexpected problem occurred. + * @throws UserNotFoundException If the user is not found. + * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. */ public boolean isMember(T userID, String groupID) - throws UserNotFoundException, TransientException, - AccessControlException + throws UserNotFoundException, TransientException, + AccessControlException { try { - String searchField = (String) userLdapAttrib.get(userID.getClass()); + String searchField = userLdapAttrib.get(userID.getClass()); if (searchField == null) { throw new IllegalArgumentException( @@ -289,26 +287,23 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO User<T> user = getUser(userID); Filter filter = Filter.createANDFilter( - Filter.createEqualityFilter(searchField, - user.getUserID().getName()), - Filter.createEqualityFilter("memberOf", groupID)); + Filter.createEqualityFilter(searchField, + user.getUserID().getName()), + Filter.createEqualityFilter("memberOf", groupID)); - SearchRequest searchRequest = - new SearchRequest(config.getUsersDN(), SearchScope.SUB, - filter, new String[] {"cn"}); + SearchRequest searchRequest = + new SearchRequest(config.getUsersDN(), SearchScope.SUB, + filter, "cn"); searchRequest.addControl( - new ProxiedAuthorizationV2RequestControl("dn:" + - getSubjectDN().toNormalizedString())); - - SearchResultEntry searchResults = + new ProxiedAuthorizationV2RequestControl("dn:" + + getSubjectDN() + .toNormalizedString())); + + SearchResultEntry searchResults = getConnection().searchForEntry(searchRequest); - - if (searchResults == null) - { - return false; - } - return true; + + return (searchResults != null); } catch (LDAPException e) { @@ -316,7 +311,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO } return false; } - + // public boolean isMember(T userID, String groupID) // throws UserNotFoundException, TransientException, // AccessControlException @@ -351,29 +346,30 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO // throw new RuntimeException("Unexpected LDAP exception", e); // } // } - + /** * Returns a member user identified by the X500Principal only. The * returned object has the fields required by the GMS. - * Note that this method binds as a proxy user and not as the + * Note that this method binds as a proxy user and not as the * subject. + * * @param userDN * @return * @throws UserNotFoundException * @throws LDAPException */ User<X500Principal> getMember(DN userDN) - throws UserNotFoundException, LDAPException + throws UserNotFoundException, LDAPException { - Filter filter = - Filter.createEqualityFilter("entrydn", - userDN.toNormalizedString()); - - SearchRequest searchRequest = - new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, + Filter filter = + Filter.createEqualityFilter("entrydn", + userDN.toNormalizedString()); + + SearchRequest searchRequest = + new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, filter, memberAttribs); - - SearchResultEntry searchResult = + + SearchResultEntry searchResult = getConnection().searchForEntry(searchRequest); if (searchResult == null) @@ -384,9 +380,9 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO } User<X500Principal> user = new User<X500Principal>( new X500Principal(searchResult.getAttributeValue( - (String) userLdapAttrib.get(X500Principal.class)))); + userLdapAttrib.get(X500Principal.class)))); String princ = searchResult.getAttributeValue( - (String) userLdapAttrib.get(HttpPrincipal.class)); + userLdapAttrib.get(HttpPrincipal.class)); if (princ != null) { user.getIdentities().add(new HttpPrincipal(princ)); @@ -396,32 +392,36 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO user.details.add(new PersonalDetails(fname, lname)); return user; } - + DN getUserDN(User<? extends Principal> user) - throws UserNotFoundException, TransientException + throws UserNotFoundException, TransientException { - String searchField = (String) userLdapAttrib.get(user.getUserID().getClass()); + String searchField = + userLdapAttrib.get(user.getUserID().getClass()); if (searchField == null) { throw new IllegalArgumentException( - "Unsupported principal type " + user.getUserID().getClass()); + "Unsupported principal type " + user.getUserID() + .getClass()); } - searchField = "(" + searchField + "=" + - user.getUserID().getName() + ")"; + searchField = "(" + searchField + "=" + + user.getUserID().getName() + ")"; SearchResultEntry searchResult = null; try { - SearchRequest searchRequest = new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, - searchField, new String[] {"entrydn"}); - + SearchRequest searchRequest = + new SearchRequest(this.config.getUsersDN(), SearchScope.SUB, + searchField, "entrydn"); - searchResult = - getConnection().searchForEntry(searchRequest); - } catch (LDAPException e) + searchResult = + getConnection().searchForEntry(searchRequest); + + } + catch (LDAPException e) { LdapDAO.checkLdapResult(e.getResultCode()); } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java index 15689805571ce6c7e8ca441ecfcc087548a7301a..32661345fc85f1042784a01421c27da98d4b9c48 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java @@ -68,17 +68,15 @@ */ package ca.nrc.cadc.ac.server.web; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.GroupReader; import ca.nrc.cadc.ac.GroupWriter; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.server.GroupPersistence; -import java.io.InputStream; -import java.security.Principal; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import javax.servlet.http.HttpServletResponse; public class CreateGroupAction extends GroupsAction { diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java index 9854b25d4e785ac066a2d6de97ed2919ea634352..e72003567a83a2723ee6ce15376d5a5beee22027 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java @@ -70,7 +70,6 @@ import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.GroupWriter; import ca.nrc.cadc.ac.server.GroupPersistence; -import javax.servlet.http.HttpServletResponse; public class GetGroupAction extends GroupsAction { diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java index 40a0d780938c326ff18fb3df7478ea264e9e75ac..a29e5fcf29a37ebf66ded5fd96cff4edbe77f26c 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java @@ -65,16 +65,21 @@ * $Revision: 4 $ * ************************************************************************ - */package ca.nrc.cadc.ac.server.web; + */ +package ca.nrc.cadc.ac.server.web; + +import java.io.Writer; import java.util.Collection; -import ca.nrc.cadc.ac.server.GroupPersistence; +import org.apache.log4j.Logger; -import com.csvreader.CsvWriter; +import ca.nrc.cadc.ac.server.GroupPersistence; public class GetGroupNamesAction extends GroupsAction { + + private static final Logger log = Logger.getLogger(GetGroupNamesAction.class); GetGroupNamesAction(GroupLogInfo logInfo) { @@ -86,16 +91,21 @@ public class GetGroupNamesAction extends GroupsAction { GroupPersistence groupPersistence = getGroupPersistence(); Collection<String> groups = groupPersistence.getGroupNames(); - response.setContentType("text/csv"); - - CsvWriter writer = new CsvWriter(response.getWriter(), ','); - - for (String group : groups) + log.debug("Found " + groups.size() + " group names"); + response.setContentType("text/plain"); + log.debug("Set content-type to text/plain"); + Writer writer = response.getWriter(); + boolean start = true; + for (final String group : groups) { + if (!start) + { + writer.write("\r\n"); + } writer.write(group); + start = false; } - writer.endRecord(); + return null; } - } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java index f71e21c28a7cd96240d8d1ee92d6a5426a6de382..83ecc9d1f7a0caae37d88e1b53d7c7183e1079e5 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java @@ -68,12 +68,16 @@ */ package ca.nrc.cadc.ac.server.web; -import ca.nrc.cadc.util.StringUtil; import java.io.IOException; +import java.net.URL; import java.net.URLDecoder; + import javax.servlet.http.HttpServletRequest; + import org.apache.log4j.Logger; +import ca.nrc.cadc.util.StringUtil; + public class GroupsActionFactory { private static final Logger log = Logger.getLogger(GroupsActionFactory.class); @@ -89,16 +93,19 @@ public class GroupsActionFactory if (path == null) { - path = new String(); + path = ""; } + if (path.startsWith("/")) { path = path.substring(1); } + if (path.endsWith("/")) { path = path.substring(0, path.length() - 1); } + String[] segments = new String[0]; if (StringUtil.hasText(path)) { @@ -109,7 +116,7 @@ public class GroupsActionFactory { if (method.equals("GET")) { - action = new ListGroupsAction(logInfo); + action = new GetGroupNamesAction(logInfo); } else if (method.equals("PUT")) { @@ -130,7 +137,16 @@ public class GroupsActionFactory } else if (method.equals("POST")) { - action = new ModifyGroupAction(logInfo, groupName, request.getRequestURI(), request.getInputStream()); + final URL requestURL = + new URL(request.getRequestURL().toString()); + final String redirectURI = requestURL.getProtocol() + "://" + + requestURL.getHost() + ":" + + requestURL.getPort() + + request.getContextPath() + + request.getServletPath() + + "/" + path; + action = new ModifyGroupAction(logInfo, groupName, redirectURI, + request.getInputStream()); } } else if (segments.length == 3) @@ -169,6 +185,7 @@ public class GroupsActionFactory if (action != null) { + log.debug("Returning action: " + action.getClass()); return action; } throw new IllegalArgumentException("Bad groups request: " + method + " on " + path); diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java index af5b564249e6c4ba4f268a8364c28e3527785923..c7a03ca5688c0d2c697ff24296428310767edc29 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java @@ -68,16 +68,14 @@ */ package ca.nrc.cadc.ac.server.web; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.GroupReader; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.server.GroupPersistence; -import java.io.InputStream; -import java.security.Principal; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import javax.servlet.http.HttpServletResponse; public class ModifyGroupAction extends GroupsAction { @@ -85,7 +83,8 @@ public class ModifyGroupAction extends GroupsAction private final String request; private final InputStream inputStream; - ModifyGroupAction(GroupLogInfo logInfo, String groupName, String request, InputStream inputStream) + ModifyGroupAction(GroupLogInfo logInfo, String groupName, + final String request, InputStream inputStream) { super(logInfo); this.groupName = groupName; @@ -99,8 +98,8 @@ public class ModifyGroupAction extends GroupsAction GroupPersistence groupPersistence = getGroupPersistence(); Group group = GroupReader.read(this.inputStream); Group oldGroup = groupPersistence.getGroup(this.groupName); - Group modifiedGroup = groupPersistence.modifyGroup(group); - + groupPersistence.modifyGroup(group); + List<String> addedMembers = new ArrayList<String>(); for (User member : group.getUserMembers()) { @@ -134,8 +133,9 @@ public class ModifyGroupAction extends GroupsAction deletedMembers = null; } logGroupInfo(group.getID(), deletedMembers, addedMembers); - + this.response.sendRedirect(request); + return null; } diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/AbstractLdapDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/AbstractLdapDAOTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5801981ac380b8f2314d4b321952cd86d15789ea --- /dev/null +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/AbstractLdapDAOTest.java @@ -0,0 +1,82 @@ +/** + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2014. (c) 2014. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + ************************************************************************ + */ + +package ca.nrc.cadc.ac.server.ldap; + +/** + * Created by jburke on 2014-11-03. + */ +public class AbstractLdapDAOTest +{ + static final String CONFIG = LdapConfig.class.getSimpleName() + ".test.properties"; + + static protected LdapConfig getLdapConfig() + { + return LdapConfig.getLdapConfig(CONFIG); + } + +} diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java index a79aa1a456ba2e629c331b1143f5d08900dec9ea..ecd65e9483ab73d8813a600fc6833ca38dbfb673 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTest.java @@ -68,38 +68,39 @@ package ca.nrc.cadc.ac.server.ldap; -import static ca.nrc.cadc.ac.server.ldap.LdapGroupDAOTest.config; -import static org.junit.Assert.assertTrue; - import java.security.PrivilegedExceptionAction; +import javax.net.ssl.SSLSocketFactory; import javax.security.auth.Subject; import javax.security.auth.x500.X500Principal; -import org.junit.Test; - import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.auth.NumericPrincipal; import com.unboundid.ldap.sdk.LDAPConnection; -public class LdapDAOTest +import org.junit.Test; +import org.junit.BeforeClass; +import static org.junit.Assert.*; + + +public class LdapDAOTest extends AbstractLdapDAOTest { - static String server = "mach275.cadc.dao.nrc.ca"; - static int port = 389; - static String adminDN = "uid=webproxy,ou=WebProxy,ou=topologymanagement,o=netscaperoot"; - static String adminPW = "go4it"; - static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; - static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; - static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net"; - - LdapConfig config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN); + static LdapConfig config; + @BeforeClass + public static void setUpBeforeClass() throws Exception + { + // get the configuration of the development server from and config files... + config = getLdapConfig(); + } @Test public void testLdapBindConnection() throws Exception { //TODO use a test user to test with. To be done when addUser available. //LdapUserDAO<X500Principal> userDAO = new LdapUserDAO<X500Principal>(); + final X500Principal subjPrincipal = new X500Principal( + "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"); // User authenticated with HttpPrincipal HttpPrincipal httpPrincipal = new HttpPrincipal("CadcDaoTest1"); @@ -115,8 +116,7 @@ public class LdapDAOTest { try { - LDAPConnection ldapCon = ldapDao.getConnection(); - assertTrue(ldapCon.isConnected()); + testConnection(ldapDao.getConnection()); return null; } catch (Exception e) @@ -126,9 +126,7 @@ public class LdapDAOTest } }); - - X500Principal subjPrincipal = new X500Principal( - "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"); + subject = new Subject(); subject.getPrincipals().add(subjPrincipal); @@ -138,8 +136,7 @@ public class LdapDAOTest { try { - LDAPConnection ldapCon = ldapDao.getConnection(); - assertTrue(ldapCon.isConnected()); + testConnection(ldapDao.getConnection()); return null; } catch (Exception e) @@ -160,8 +157,7 @@ public class LdapDAOTest try { - LDAPConnection ldapCon = ldapDao.getConnection(); - assertTrue(ldapCon.isConnected()); + testConnection(ldapDao.getConnection()); return null; } catch (Exception e) @@ -172,4 +168,10 @@ public class LdapDAOTest }); } + + private void testConnection(final LDAPConnection ldapCon) + { + assertTrue("Not connected but should be.", ldapCon.isConnected()); + } + } diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTestImpl.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTestImpl.java index cf3c8d2e2e4eb5b87e7adbe9ef8631173ce8a4f9..7d3be479f44fc01e2eaaab7805e645a1dc9f573a 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTestImpl.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapDAOTestImpl.java @@ -66,7 +66,6 @@ */ - package ca.nrc.cadc.ac.server.ldap; import java.security.AccessControlException; @@ -74,16 +73,17 @@ import java.security.AccessControlException; import com.unboundid.ldap.sdk.LDAPConnection; import com.unboundid.ldap.sdk.LDAPException; + public class LdapDAOTestImpl extends LdapDAO { public LdapDAOTestImpl(LdapConfig config) { super(config); } - + @Override public LDAPConnection getConnection() throws LDAPException, - AccessControlException + AccessControlException { return super.getConnection(); } diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java index e29da595b841c4ee6651ae5a5c1c1a3bb780082e..7af54bc0ae0a9ec3c1e3621a31038538440a7ae9 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAOTest.java @@ -1,34 +1,67 @@ -/* +/** ************************************************************************ - **** C A N A D I A N A S T R O N O M Y D A T A C E N T R E ***** + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** * - * (c) 2014. (c) 2014. - * National Research Council Conseil national de recherches - * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 - * All rights reserved Tous droits reserves + * (c) 2014. (c) 2014. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés * - * NRC disclaims any warranties Le CNRC denie toute garantie - * expressed, implied, or statu- enoncee, implicite ou legale, - * tory, of any kind with respect de quelque nature que se soit, - * to the software, including concernant le logiciel, y com- - * without limitation any war- pris sans restriction toute - * ranty of merchantability or garantie de valeur marchande - * fitness for a particular pur- ou de pertinence pour un usage - * pose. NRC shall not be liable particulier. Le CNRC ne - * in any event for any damages, pourra en aucun cas etre tenu - * whether direct or indirect, responsable de tout dommage, - * special or general, consequen- direct ou indirect, particul- - * tial or incidental, arising ier ou general, accessoire ou - * from the use of the software. fortuit, resultant de l'utili- - * sation du logiciel. + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. * - * @author adriand - * - * @version $Revision: $ - * - * - **** C A N A D I A N A S T R O N O M Y D A T A C E N T R E ***** ************************************************************************ */ @@ -61,20 +94,10 @@ import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.util.Log4jInit; import static org.junit.Assert.assertNotNull; -public class LdapGroupDAOTest +public class LdapGroupDAOTest extends AbstractLdapDAOTest { private static final Logger log = Logger.getLogger(LdapGroupDAOTest.class); - static String server = "mach275.cadc.dao.nrc.ca"; - static int port = 389; - static String adminDN = "uid=webproxy,ou=webproxy,ou=topologymanagement,o=netscaperoot"; - static String adminPW = "go4it"; - static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; - static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; - static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net"; -// static String usersDN = "ou=Users,ou=ds,dc=canfar,dc=net"; -// static String groupsDN = "ou=Groups,ou=ds,dc=canfar,dc=net"; - static String daoTestDN1 = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"; static String daoTestDN2 = "cn=cadcdaotest2,ou=cadc,o=hia,c=ca"; static String daoTestDN3 = "cn=cadcdaotest3,ou=cadc,o=hia,c=ca"; @@ -84,37 +107,36 @@ public class LdapGroupDAOTest static X500Principal daoTestPrincipal2; static X500Principal daoTestPrincipal3; static X500Principal unknownPrincipal; - static X500Principal adminPrincipal; - + static User<X500Principal> daoTestUser1; static User<X500Principal> daoTestUser2; static User<X500Principal> daoTestUser3; static User<X500Principal> unknownUser; - static User<X500Principal> adminUser; - + static Subject daoTestUser1Subject; static Subject daoTestUser2Subject; static Subject anonSubject; - + static LdapConfig config; @BeforeClass public static void setUpBeforeClass() throws Exception { - Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG); + Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO); + + // get the configuration of the development server from and config files... + config = getLdapConfig(); daoTestPrincipal1 = new X500Principal(daoTestDN1); daoTestPrincipal2 = new X500Principal(daoTestDN2); daoTestPrincipal3 = new X500Principal(daoTestDN3); unknownPrincipal = new X500Principal(unknownDN); - adminPrincipal = new X500Principal(adminDN); daoTestUser1 = new User<X500Principal>(daoTestPrincipal1); daoTestUser2 = new User<X500Principal>(daoTestPrincipal2); daoTestUser3 = new User<X500Principal>(daoTestPrincipal3); unknownUser = new User<X500Principal>(unknownPrincipal); - adminUser = new User<X500Principal>(adminPrincipal); daoTestUser1Subject = new Subject(); daoTestUser1Subject.getPrincipals().add(daoTestUser1.getUserID()); @@ -124,8 +146,6 @@ public class LdapGroupDAOTest anonSubject = new Subject(); anonSubject.getPrincipals().add(unknownUser.getUserID()); - - config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN); } LdapGroupDAO<X500Principal> getGroupDAO() @@ -185,12 +205,24 @@ public class LdapGroupDAOTest expectGroup.getGroupMembers().remove(otherGroup); actualGroup = getGroupDAO().modifyGroup(expectGroup); assertGroupsEqual(expectGroup, actualGroup); - - // delete the group + expectGroup.description = "Happy testing"; expectGroup.getUserMembers().add(daoTestUser2); expectGroup.getGroupMembers().add(otherGroup); - + + // userAdmins + expectGroup.getUserAdmins().add(daoTestUser3); + actualGroup = getGroupDAO().modifyGroup(expectGroup); + assertGroupsEqual(expectGroup, actualGroup); + + // groupAdmins + Group adminGroup = new Group(getGroupID(), daoTestUser1); + adminGroup = getGroupDAO().addGroup(adminGroup); + expectGroup.getGroupAdmins().add(adminGroup); + actualGroup = getGroupDAO().modifyGroup(expectGroup); + assertGroupsEqual(expectGroup, actualGroup); + + // delete the group getGroupDAO().deleteGroup(expectGroup.getID()); try { @@ -315,7 +347,7 @@ public class LdapGroupDAOTest assertNotNull(groups); assertTrue(groups.size() >= 2); - log.debug("# groups found: " + groups.size()); + log.debug("testSearchMemberGroups groups found: " + groups.size()); boolean found1 = false; boolean found2 = false; for (Group group : groups) @@ -413,7 +445,7 @@ public class LdapGroupDAOTest getGroupDAO().getGroups(daoTestUser2.getUserID(), Role.ADMIN, null); - log.debug("# groups found: " + groups.size()); + log.debug("testSearchAdminGroups groups found: " + groups.size()); assertNotNull(groups); assertTrue(groups.size() >= 2); @@ -471,6 +503,97 @@ public class LdapGroupDAOTest } }); } + + @Test + public void testGetGroupNames() throws Exception + { + final String groupID = getGroupID(); + final String testGroup1ID = groupID + ".1"; + final String testGroup2ID = groupID + ".2"; + + Subject.doAs(daoTestUser1Subject, new PrivilegedExceptionAction<Object>() + { + public Object run() throws Exception + { + try + { + Group testGroup1 = new Group(testGroup1ID, daoTestUser1); + testGroup1 = getGroupDAO().addGroup(testGroup1); + log.debug("add group: " + testGroup1ID); + + Group testGroup2 = new Group(testGroup2ID, daoTestUser1); + testGroup2 = getGroupDAO().addGroup(testGroup2); + log.debug("add group: " + testGroup2ID); + //Thread.sleep(1000); // sleep to let memberof plugin do its work + } + catch (Exception e) + { + throw new Exception("Problems", e); + } + return null; + } + }); + + Subject.doAs(daoTestUser1Subject, new PrivilegedExceptionAction<Object>() + { + public Object run() throws Exception + { + try + { + Collection<String> groups = getGroupDAO().getGroupNames(); + + log.debug("testGetGroupNames groups found: " + groups.size()); + assertNotNull(groups); + assertTrue(groups.size() >= 2); + + boolean found1 = false; + boolean found2 = false; + for (String name : groups) + { + log.debug("group: " + name); + if (name.equals(testGroup1ID)) + { + found1 = true; + } + if (name.equals(testGroup2ID)) + { + found2 = true; + } + } + if (!found1) + { + fail("Admin group " + testGroup1ID + " not found"); + } + if (!found2) + { + fail("Admin group " + testGroup2ID + " not found"); + } + } + catch (Exception e) + { + throw new Exception("Problems", e); + } + return null; + } + }); + + Subject.doAs(daoTestUser1Subject, new PrivilegedExceptionAction<Object>() + { + public Object run() throws Exception + { + try + { + getGroupDAO().deleteGroup(testGroup1ID); + getGroupDAO().deleteGroup(testGroup2ID); + } + catch (Exception e) + { + throw new Exception("Problems", e); + } + return null; + } + }); + } @Test public void testAddGroupExceptions() throws Exception @@ -570,23 +693,24 @@ public class LdapGroupDAOTest return null; } }); - - Subject.doAs(daoTestUser2Subject, new PrivilegedExceptionAction<Object>() - { - public Object run() throws Exception - { - try - { - getGroupDAO().getGroup(groupID); - fail("getGroup with anonymous access should throw " + - "AccessControlException"); - } - catch (AccessControlException ignore) {} - return null; - } - }); + + // All access ACI's will allow anonymous access +// Subject.doAs(daoTestUser2Subject, new PrivilegedExceptionAction<Object>() +// { +// public Object run() throws Exception +// { +// try +// { +// getGroupDAO().getGroup(groupID); +// fail("getGroup with anonymous access should throw " + +// "AccessControlException"); +// } +// catch (AccessControlException ignore) {} +// return null; +// } +// }); } - + @Test public void testModifyGroupExceptions() throws Exception { @@ -699,7 +823,7 @@ public class LdapGroupDAOTest fail("searchGroups with unknown user should throw " + "UserNotFoundException"); } - catch (UserNotFoundException ignore) {} + catch (AccessControlException ignore) {} try { @@ -728,38 +852,35 @@ public class LdapGroupDAOTest return null; } }); - - Subject.doAs(daoTestUser1Subject, new PrivilegedExceptionAction<Object>() - { - public Object run() throws Exception - { - getGroupDAO().deleteGroup(groupID); - return null; - } - }); - - + + // // change the user - Subject.doAs(daoTestUser2Subject, new PrivilegedExceptionAction<Object>() +// Subject.doAs(daoTestUser2Subject, new PrivilegedExceptionAction<Object>() +// { +// public Object run() throws Exception +// { +// try +// { +// Group group = getGroupDAO().getGroup(groupID); +// assertTrue(group == null); +// +// fail("searchGroups with un-authorized user should throw " + +// "AccessControlException"); +// } +// catch (AccessControlException ignore) +// { +// +// } +// +// return null; +// } +// }); + + Subject.doAs(daoTestUser1Subject, new PrivilegedExceptionAction<Object>() { - public Object run() throws Exception { - - - try - { - Group group = getGroupDAO().getGroup(groupID); - assertTrue(group == null); - - fail("searchGroups with un-authorized user should throw " + - "AccessControlException"); - } - catch (AccessControlException ignore) - { - - } - + getGroupDAO().deleteGroup(groupID); return null; } }); @@ -775,12 +896,14 @@ public class LdapGroupDAOTest assertEquals(gr1.getID(), gr2.getID()); assertEquals(gr1.description, gr2.description); assertEquals(gr1.getOwner(), gr2.getOwner()); + assertEquals(gr1.getGroupMembers(), gr2.getGroupMembers()); assertEquals(gr1.getGroupMembers().size(), gr2.getGroupMembers().size()); for (Group gr : gr1.getGroupMembers()) { assertTrue(gr2.getGroupMembers().contains(gr)); } + assertEquals(gr1.getUserMembers(), gr2.getUserMembers()); assertEquals(gr1.getUserMembers().size(), gr2.getUserMembers() .size()); @@ -788,6 +911,22 @@ public class LdapGroupDAOTest { assertTrue(gr2.getUserMembers().contains(user)); } + + assertEquals(gr1.getGroupAdmins(), gr2.getGroupAdmins()); + assertEquals(gr1.getGroupAdmins().size(), gr2.getGroupAdmins().size()); + for (Group gr : gr1.getGroupAdmins()) + { + assertTrue(gr2.getGroupAdmins().contains(gr)); + } + + assertEquals(gr1.getUserAdmins(), gr2.getUserAdmins()); + assertEquals(gr1.getUserAdmins().size(), gr2.getUserAdmins() + .size()); + for (User<?> user : gr1.getUserAdmins()) + { + assertTrue(gr2.getUserAdmins().contains(user)); + } + assertEquals(gr1.getProperties(), gr2.getProperties()); for (GroupProperty prop : gr1.getProperties()) { diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java index 6bda5b89871e4aaa8bbd6ffe71f3dc56cfa6e809..abce3f7810d0941c878c2ea03a98de9c093dfc00 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java @@ -85,38 +85,21 @@ import org.apache.log4j.Logger; import org.junit.BeforeClass; import org.junit.Test; -import ca.nrc.cadc.ac.Group; import ca.nrc.cadc.ac.PersonalDetails; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserDetails; import ca.nrc.cadc.auth.HttpPrincipal; -import ca.nrc.cadc.auth.NumericPrincipal; import ca.nrc.cadc.util.Log4jInit; import com.unboundid.ldap.sdk.DN; -/** - * - * @author jburke - */ -public class LdapUserDAOTest +public class LdapUserDAOTest extends AbstractLdapDAOTest { private static final Logger log = Logger.getLogger(LdapUserDAOTest.class); - - static String server = "mach275.cadc.dao.nrc.ca"; - static int port = 389; - static String adminDN = "uid=webproxy,ou=Webproxy,ou=topologymanagement,o=netscaperoot"; - static String adminPW = "go4it"; - static String usersDN = "ou=Users,ou=ds,dc=canfartest,dc=net"; - static String groupsDN = "ou=Groups,ou=ds,dc=canfartest,dc=net"; - static String adminGroupsDN = "ou=adminGroups,ou=ds,dc=canfartest,dc=net"; -// static String userBaseDN = "ou=Users,ou=ds,dc=canfar,dc=net"; -// static String groupBaseDN = "ou=Groups,ou=ds,dc=canfar,dc=net"; - + static final String testUserX509DN = "cn=cadcdaotest1,ou=cadc,o=hia,c=ca"; - static final String testUserDN = "uid=cadcdaotest1," + usersDN; - - + + static String testUserDN; static User<X500Principal> testUser; static LdapConfig config; @@ -124,14 +107,16 @@ public class LdapUserDAOTest public static void setUpBeforeClass() throws Exception { - Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG); - + Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO); + + // get the configuration of the development server from and config files... + config = getLdapConfig(); + testUser = new User<X500Principal>(new X500Principal(testUserX509DN)); - - config = new LdapConfig(server, port, adminDN, adminPW, usersDN, groupsDN, adminGroupsDN); - testUser.details.add(new PersonalDetails("CADC", "DAOTest1")); - testUser.getIdentities().add(new HttpPrincipal("CadcDaoTest1")); + testUser.getIdentities().add(new HttpPrincipal("CadcDaoTest1")); + + testUserDN = "uid=cadcdaotest1," + config.getUsersDN(); } LdapUserDAO<X500Principal> getUserDAO() @@ -226,7 +211,7 @@ public class LdapUserDAOTest boolean isMember = getUserDAO().isMember(testUser.getUserID(), "foo"); assertFalse(isMember); - String groupDN = "cn=cadcdaotestgroup1,ou=groups,ou=ds,dc=canfartest,dc=net"; + String groupDN = "cn=cadcdaotestgroup1," + config.getGroupsDN(); isMember = getUserDAO().isMember(testUser.getUserID(), groupDN); assertTrue(isMember); diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GetGroupNamesActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GetGroupNamesActionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0cb59383950ce877c3c5c88a4ee1da506adecce7 --- /dev/null +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GetGroupNamesActionTest.java @@ -0,0 +1,159 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2014. (c) 2014. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * $Revision: 4 $ + * + ************************************************************************ + */ +package ca.nrc.cadc.ac.server.web; + +import ca.nrc.cadc.ac.server.GroupPersistence; +import ca.nrc.cadc.util.Log4jInit; +import ca.nrc.cadc.uws.server.SyncOutput; + +import org.apache.log4j.Level; +import org.apache.log4j.Logger; +import org.easymock.EasyMock; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; + +import static org.junit.Assert.fail; + +/** + * + * @author jburke + */ +public class GetGroupNamesActionTest +{ + private final static Logger log = Logger.getLogger(GetGroupNamesActionTest.class); + + @BeforeClass + public static void setUpClass() + { + Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO); + } + + @Test + @Ignore + public void testRun() throws Exception + { + try + { + Collection<String> groupNames = new ArrayList<String>(); + groupNames.add("foo"); + groupNames.add("bar"); + + final GroupPersistence mockPersistence = EasyMock.createMock(GroupPersistence.class); + EasyMock.expect(mockPersistence.getGroupNames()).andReturn(groupNames).once(); + + final PrintWriter mockWriter = EasyMock.createMock(PrintWriter.class); + mockWriter.write("foo", 0, 3); + EasyMock.expectLastCall(); + mockWriter.write(44); + EasyMock.expectLastCall(); + mockWriter.write("bar", 0, 3); + EasyMock.expectLastCall(); + mockWriter.write("\n"); + EasyMock.expectLastCall(); + + final SyncOutput mockSyncOutput = + EasyMock.createMock(SyncOutput.class); + + mockSyncOutput.setHeader("Content-Type", "text/csv"); + + final HttpServletResponse mockResponse = EasyMock.createMock(HttpServletResponse.class); + mockResponse.setContentType("text/csv"); + EasyMock.expectLastCall(); + EasyMock.expect(mockResponse.getWriter()).andReturn(mockWriter).once(); + + GroupLogInfo mockLog = EasyMock.createMock(GroupLogInfo.class); + + EasyMock.replay(mockPersistence, mockWriter, mockResponse, mockLog); + + GetGroupNamesAction action = new GetGroupNamesAction(mockLog) + { + @Override + <T extends Principal> GroupPersistence<T> getGroupPersistence() + { + return mockPersistence; + }; + }; + + action.run(); + } + catch (Throwable t) + { + log.error(t.getMessage(), t); + fail("unexpected error: " + t.getMessage()); + } + } + +} diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupActionFactoryTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupActionFactoryTest.java index 22df7faa1794f7970cc14064ca050b3a7e9584a7..f62d030cc6b0f9131a9681250664fbee8a97444c 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupActionFactoryTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupActionFactoryTest.java @@ -77,6 +77,8 @@ import org.junit.Test; import ca.nrc.cadc.util.Log4jInit; +import java.net.URL; + public class GroupActionFactoryTest { private final static Logger log = Logger.getLogger(GroupActionFactoryTest.class); @@ -189,7 +191,7 @@ public class GroupActionFactoryTest } @Test - public void testCreateListGroupsAction() + public void testCreateGetGroupNamesAction() { try { @@ -199,7 +201,7 @@ public class GroupActionFactoryTest EasyMock.replay(request); GroupsAction action = GroupsActionFactory.getGroupsAction(request, null); EasyMock.verify(request); - Assert.assertTrue("Wrong action", action instanceof ListGroupsAction); + Assert.assertTrue("Wrong action", action instanceof GetGroupNamesAction); } catch (Throwable t) { @@ -213,10 +215,15 @@ public class GroupActionFactoryTest { try { + StringBuffer sb = new StringBuffer(); + sb.append("http://localhost:80/ac/groups/foo"); + HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class); EasyMock.expect(request.getPathInfo()).andReturn("groupName"); EasyMock.expect(request.getMethod()).andReturn("POST"); - EasyMock.expect(request.getRequestURI()).andReturn(null); + EasyMock.expect(request.getRequestURL()).andReturn(sb); + EasyMock.expect(request.getContextPath()).andReturn(""); + EasyMock.expect(request.getServletPath()).andReturn(""); EasyMock.expect(request.getInputStream()).andReturn(null); EasyMock.replay(request); GroupsAction action = GroupsActionFactory.getGroupsAction(request, null); diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupsActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupsActionTest.java index d7b46ae5f7c7efc6f1d8e46abe0c21d73fe513a0..08b28e9725675f4d955688e194198862144b9f42 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupsActionTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/GroupsActionTest.java @@ -75,14 +75,19 @@ import ca.nrc.cadc.ac.MemberNotFoundException; import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.net.TransientException; import ca.nrc.cadc.util.Log4jInit; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.security.AccessControlException; +import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.easymock.EasyMock; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; @@ -144,7 +149,7 @@ public class GroupsActionTest Exception e = new UserNotFoundException("foo"); testDoAction(message, responseCode, e); } - + @Test public void testDoActionMemberAlreadyExistsException() throws Exception { @@ -186,14 +191,14 @@ public class GroupsActionTest response.setStatus(503); EasyMock.expectLastCall().once(); EasyMock.replay(response); - + GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class); logInfo.setSuccess(false); EasyMock.expectLastCall().once(); logInfo.setMessage("Internal Transient Error: foo"); EasyMock.expectLastCall().once(); EasyMock.replay(logInfo); - + GroupsActionImpl action = new GroupsActionImpl(logInfo); action.setException(new TransientException("foo")); action.doAction(null, response); @@ -205,7 +210,8 @@ public class GroupsActionTest } } - private void testDoAction(String message, int responseCode, Exception e) throws Exception + private void testDoAction(String message, int responseCode, Exception e) + throws Exception { try { @@ -218,12 +224,12 @@ public class GroupsActionTest response.setStatus(responseCode); EasyMock.expectLastCall().once(); EasyMock.replay(response); - + GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class); logInfo.setMessage(message); EasyMock.expectLastCall().once(); EasyMock.replay(logInfo); - + GroupsActionImpl action = new GroupsActionImpl(logInfo); action.setException(e); action.doAction(null, response); diff --git a/projects/cadcAccessControl/build.xml b/projects/cadcAccessControl/build.xml index 7b1f362a954dde0e97f2e3273e512eee538ae873..a3b964ed3878c53e9c2aec9a667ebce0cc72ca79 100644 --- a/projects/cadcAccessControl/build.xml +++ b/projects/cadcAccessControl/build.xml @@ -91,11 +91,12 @@ <property name="cadcRegistryClient" value="${lib}/cadcRegistryClient.jar" /> <property name="jdom2" value="${ext.lib}/jdom2.jar" /> + <property name="javacsv" value="${ext.lib}/javacsv.jar" /> <property name="log4j" value="${ext.lib}/log4j.jar" /> <property name="unboundid" value="${ext.lib}/unboundid-ldapsdk-se.jar" /> - <property name="jars" value="${cadcUtil}:${cadcRegistryClient}:${jdom2}:${log4j}:${unboundid}" /> + <property name="jars" value="${jdom2}:${log4j}:${javacsv}:${unboundid}:${cadcUtil}:${cadcRegistryClient}" /> <target name="build" depends="compile"> <jar jarfile="${build}/lib/${project}.jar" diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java index afbf97f64dd5d701898d16acf821a572b58d9e4a..cacb9a00c2efacc78ee92aa9d23ed35449fe38d9 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java @@ -85,7 +85,7 @@ public class AC // Denotes a group readable by public public static final String PROPERTY_PUBLIC = "ivo://ivoa.net/gms#public"; - public static final String GMS_SERVICE_URI = "ivo://cadc.nrc.ca/gms"; + public static final String GMS_SERVICE_URI = "ivo://cadc.nrc.ca/canfargms"; // Group URI attribute once the group name is appended public static final String GROUP_URI = "ivo://cadc.nrc.ca/gms#"; diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java index 8061f48773eeb3913a4b8020aba06af37e703fed..70ef1830f10432669f5b0bbd27be53286fa8d2b4 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java @@ -119,7 +119,7 @@ public class GroupReader * @throws java.net.URISyntaxException */ public static Group read(InputStream in) - throws ReaderException, IOException, URISyntaxException + throws ReaderException, IOException { if (in == null) { @@ -147,7 +147,7 @@ public class GroupReader * @throws java.net.URISyntaxException */ public static Group read(Reader reader) - throws ReaderException, IOException, URISyntaxException + throws ReaderException, IOException { if (reader == null) { @@ -179,7 +179,7 @@ public class GroupReader } protected static Group parseGroup(Element groupElement) - throws URISyntaxException, ReaderException + throws ReaderException { String uri = groupElement.getAttributeValue("uri"); if (uri == null) diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java index 6f1489c9f4999d2fcef6409d95c563622c3d18dc..7609ee7326133d0d2975059fdd7666c0e3435f0c 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java @@ -68,9 +68,12 @@ */ package ca.nrc.cadc.ac.client; +import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -79,6 +82,7 @@ import java.security.AccessControlContext; import java.security.AccessControlException; import java.security.AccessController; import java.security.Principal; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -104,8 +108,10 @@ import ca.nrc.cadc.auth.SSLUtil; import ca.nrc.cadc.net.HttpDownload; import ca.nrc.cadc.net.HttpPost; import ca.nrc.cadc.net.HttpUpload; +import ca.nrc.cadc.net.InputStreamWrapper; import ca.nrc.cadc.net.NetUtil; + /** * Client class for performing group searching and group actions * with the access control web service. @@ -115,7 +121,8 @@ public class GMSClient private static final Logger log = Logger.getLogger(GMSClient.class); // socket factory to use when connecting - public SSLSocketFactory sslSocketFactory; + private SSLSocketFactory sslSocketFactory; + private SSLSocketFactory mySocketFactory; private String baseURL; @@ -134,12 +141,7 @@ public class GMSClient } try { - URL testURL = new URL(baseURL); - if (!testURL.getProtocol().equals("https")) - { - throw new IllegalArgumentException( - "URL must have HTTPS protocol"); - } + new URL(baseURL); } catch (MalformedURLException e) { @@ -229,7 +231,7 @@ public class GMSClient String retXML = transfer.getResponseBody(); try { - log.debug("createGroup returned: " + groupXML); + log.debug("createGroup returned: " + retXML); return GroupReader.read(retXML); } catch (Exception bug) @@ -262,7 +264,7 @@ public class GMSClient Throwable error = transfer.getThrowable(); if (error != null) { - log.debug("getGroup throwable", error); + log.debug("getGroup throwable (" + transfer.getResponseCode() + ")", error); // transfer returns a -1 code for anonymous access. if ((transfer.getResponseCode() == -1) || (transfer.getResponseCode() == 401) || @@ -293,6 +295,74 @@ public class GMSClient throw new RuntimeException(bug); } } + + /** + * Get the all group names. + * + * @return The list of names. + * @throws AccessControlException If unauthorized to perform this operation. + * @throws java.io.IOException + */ + public List<String> getGroupNames() + throws AccessControlException, IOException + { + final URL getGroupNamesURL = new URL(this.baseURL + "/groups"); + log.debug("getGroupNames request to " + getGroupNamesURL.toString()); + + final List<String> groupNames = new ArrayList<String>(); + final HttpDownload httpDownload = + new HttpDownload(getGroupNamesURL, new InputStreamWrapper() + { + @Override + public void read(final InputStream inputStream) throws IOException + { + try + { + InputStreamReader inReader = new InputStreamReader(inputStream); + BufferedReader reader = new BufferedReader(inReader); + String line; + while ((line = reader.readLine()) != null) { + groupNames.add(line); + } + } + catch (Exception bug) + { + log.error("Unexpected exception", bug); + throw new RuntimeException(bug); + } + } + }); + + httpDownload.setSSLSocketFactory(getSSLSocketFactory()); + httpDownload.run(); + + final Throwable error = httpDownload.getThrowable(); + + if (error != null) + { + final String errMessage = error.getMessage(); + final int responseCode = httpDownload.getResponseCode(); + + log.debug("getGroupNames response " + responseCode + ": " + + errMessage); + + if ((responseCode == 401) || (responseCode == 403) || + (responseCode == -1)) + { + throw new AccessControlException(errMessage); + } + if (responseCode == 400) + { + throw new IllegalArgumentException(errMessage); + } + throw new IOException("HttpResponse (" + responseCode + ") - " + errMessage); + } + + log.debug("Content-Length: " + httpDownload.getContentLength()); + log.debug("Content-Type: " + httpDownload.getContentType()); + + return groupNames; + } /** * Update a group. @@ -301,11 +371,11 @@ public class GMSClient * @return The group after update. * @throws IllegalArgumentException If cyclical membership is detected. * @throws GroupNotFoundException If the group was not found. - * @throws GroupNotFoundException If a member was not found. + * @throws UserNotFoundException If a member was not found. * @throws AccessControlException If unauthorized to perform this operation. * @throws java.io.IOException */ - public Group updateGroup(Group group) + public void updateGroup(Group group) throws IllegalArgumentException, GroupNotFoundException, UserNotFoundException, AccessControlException, IOException { @@ -320,7 +390,7 @@ public class GMSClient log.debug("updateGroup: " + groupXML); HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(), - "application/xml", true); + "application/xml", false); transfer.setSSLSocketFactory(getSSLSocketFactory()); transfer.run(); @@ -328,11 +398,6 @@ public class GMSClient Throwable error = transfer.getThrowable(); if (error != null) { - log.debug("updateGroup throwable", error); - if (transfer.getResponseCode() == 302) - { - return getGroup(group.getID()); - } // transfer returns a -1 code for anonymous access. if ((transfer.getResponseCode() == -1) || (transfer.getResponseCode() == 401) || @@ -353,18 +418,6 @@ public class GMSClient } throw new IOException(error); } - - String retXML = transfer.getResponseBody(); - try - { - log.debug("updateGroup returned: " + groupXML); - return GroupReader.read(retXML); - } - catch (Exception bug) - { - log.error("Unexpected exception", bug); - throw new RuntimeException(bug); - } } /** @@ -383,7 +436,7 @@ public class GMSClient // reset the state of the cache clearCache(); - + HttpURLConnection conn = (HttpURLConnection) deleteGroupURL.openConnection(); conn.setRequestMethod("DELETE"); @@ -394,7 +447,9 @@ public class GMSClient ((HttpsURLConnection) conn) .setSSLSocketFactory(sf); } - int responseCode = -1; + + final int responseCode; + try { responseCode = conn.getResponseCode(); @@ -449,30 +504,17 @@ public class GMSClient // reset the state of the cache clearCache(); - HttpURLConnection conn = - (HttpURLConnection) addGroupMemberURL.openConnection(); - conn.setRequestMethod("PUT"); + final InputStream is = new ByteArrayInputStream(new byte[0]); + final HttpUpload httpUpload = new HttpUpload(is, addGroupMemberURL); - SSLSocketFactory sf = getSSLSocketFactory(); - if ((sf != null) && ((conn instanceof HttpsURLConnection))) - { - ((HttpsURLConnection) conn) - .setSSLSocketFactory(getSSLSocketFactory()); - } - - // Try to handle anonymous access and throw AccessControlException - int responseCode = -1; - try - { - responseCode = conn.getResponseCode(); - } - catch (Exception ignore) {} - - if ((responseCode != 200) && (responseCode != 201)) + httpUpload.setSSLSocketFactory(getSSLSocketFactory()); + httpUpload.run(); + + final Throwable error = httpUpload.getThrowable(); + if (error != null) { - String errMessage = NetUtil.getErrorBody(conn); - log.debug("addGroupMember response " + responseCode + ": " + - errMessage); + final int responseCode = httpUpload.getResponseCode(); + final String errMessage = error.getMessage(); if ((responseCode == -1) || (responseCode == 401) || @@ -498,7 +540,7 @@ public class GMSClient * @param targetGroupName The group in which to add the group member. * @param userID The user to add. * @throws GroupNotFoundException If the group was not found. - * @throws GroupNotFoundException If the member was not found. + * @throws UserNotFoundException If the member was not found. * @throws java.io.IOException * @throws AccessControlException If unauthorized to perform this operation. */ @@ -506,7 +548,7 @@ public class GMSClient throws GroupNotFoundException, UserNotFoundException, AccessControlException, IOException { String userIDType = AuthenticationUtil.getPrincipalType(userID); - String encodedUserID = URLEncoder.encode(userID.toString(), "UTF-8"); + String encodedUserID = URLEncoder.encode(userID.getName(), "UTF-8"); URL addUserMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName + "/userMembers/" + encodedUserID + "?idType=" + userIDType); @@ -516,30 +558,17 @@ public class GMSClient // reset the state of the cache clearCache(); - HttpURLConnection conn = - (HttpURLConnection) addUserMemberURL.openConnection(); - conn.setRequestMethod("PUT"); + final InputStream is = new ByteArrayInputStream(new byte[0]); + final HttpUpload httpUpload = new HttpUpload(is, addUserMemberURL); - SSLSocketFactory sf = getSSLSocketFactory(); - if ((sf != null) && ((conn instanceof HttpsURLConnection))) - { - ((HttpsURLConnection) conn) - .setSSLSocketFactory(getSSLSocketFactory()); - } - - // Try to handle anonymous access and throw AccessControlException - int responseCode = -1; - try - { - responseCode = conn.getResponseCode(); - } - catch (Exception ignore) {} + httpUpload.setSSLSocketFactory(getSSLSocketFactory()); + httpUpload.run(); - if ((responseCode != 200) && (responseCode != 201)) + final Throwable error = httpUpload.getThrowable(); + if (error != null) { - String errMessage = NetUtil.getErrorBody(conn); - log.debug("addUserMember response " + responseCode + ": " + - errMessage); + final int responseCode = httpUpload.getResponseCode(); + final String errMessage = error.getMessage(); if ((responseCode == -1) || (responseCode == 401) || @@ -647,7 +676,7 @@ public class GMSClient encodedUserID + "?idType=" + userIDType); - log.debug("removeUserMember request to " + + log.debug("removeUserMember request to " + removeUserMemberURL.toString()); // reset the state of the cache @@ -731,9 +760,11 @@ public class GMSClient StringBuilder searchGroupURL = new StringBuilder(this.baseURL); searchGroupURL.append("/search?"); - searchGroupURL.append("ID=" + URLEncoder.encode(id, "UTF-8")); - searchGroupURL.append("&IDTYPE=" + URLEncoder.encode(idType, "UTF-8")); - searchGroupURL.append("&ROLE=" + URLEncoder.encode(roleString, "UTF-8")); + searchGroupURL.append("ID=").append(URLEncoder.encode(id, "UTF-8")); + searchGroupURL.append("&IDTYPE=") + .append(URLEncoder.encode(idType, "UTF-8")); + searchGroupURL.append("&ROLE=") + .append(URLEncoder.encode(roleString, "UTF-8")); log.debug("getMemberships request to " + searchGroupURL.toString()); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -826,7 +857,7 @@ public class GMSClient List<Group> cachedGroups = getCachedGroups(userID, role); if (cachedGroups != null) { - int index = cachedGroups.indexOf(groupName); + int index = cachedGroups.indexOf(new Group(groupName)); if (index != -1) { return cachedGroups.get(index); @@ -843,11 +874,14 @@ public class GMSClient StringBuilder searchGroupURL = new StringBuilder(this.baseURL); searchGroupURL.append("/search?"); - - searchGroupURL.append("ID=" + URLEncoder.encode(id, "UTF-8")); - searchGroupURL.append("&IDTYPE=" + URLEncoder.encode(idType, "UTF-8")); - searchGroupURL.append("&ROLE=" + URLEncoder.encode(roleString, "UTF-8")); - searchGroupURL.append("&GROUPID=" + URLEncoder.encode(groupName, "UTF-8")); + + searchGroupURL.append("ID=").append(URLEncoder.encode(id, "UTF-8")); + searchGroupURL.append("&IDTYPE=") + .append(URLEncoder.encode(idType, "UTF-8")); + searchGroupURL.append("&ROLE=") + .append(URLEncoder.encode(roleString, "UTF-8")); + searchGroupURL.append("&GROUPID=") + .append(URLEncoder.encode(groupName, "UTF-8")); log.debug("getMembership request to " + searchGroupURL.toString()); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -947,23 +981,41 @@ public class GMSClient */ public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) { + if (mySocketFactory != null) + throw new IllegalStateException("Illegal use of GMSClient: " + + "cannot set SSLSocketFactory after using one created from Subject"); this.sslSocketFactory = sslSocketFactory; + clearCache(); } - /** - * @return the sslSocketFactory - */ + private int subjectHashCode = 0; private SSLSocketFactory getSSLSocketFactory() { - if (this.sslSocketFactory == null) + AccessControlContext ac = AccessController.getContext(); + Subject s = Subject.getSubject(ac); + + // no real Subject: can only use the one from setSSLSocketFactory + if (s == null || s.getPrincipals().isEmpty()) + { + return sslSocketFactory; + } + + // lazy init + if (this.mySocketFactory == null) + { + log.debug("getSSLSocketFactory: " + s); + this.mySocketFactory = SSLUtil.getSocketFactory(s); + this.subjectHashCode = s.hashCode(); + } + else { - log.debug("initHTTPS: lazy init"); - AccessControlContext ac = AccessController.getContext(); - Subject s = Subject.getSubject(ac); - this.sslSocketFactory = SSLUtil.getSocketFactory(s); - log.debug("Socket Factory: " + this.sslSocketFactory); + int c = s.hashCode(); + if (c != subjectHashCode) + throw new IllegalStateException("Illegal use of " + + this.getClass().getSimpleName() + + ": subject change not supported for internal SSLSocketFactory"); } - return this.sslSocketFactory; + return this.mySocketFactory; } protected void clearCache() @@ -1008,7 +1060,7 @@ public class GMSClient { log.debug("Caching groups for " + userID + ", role " + role); - GroupMemberships groupCredentials = null; + final GroupMemberships groupCredentials; Set groupCredentialSet = subject.getPrivateCredentials(GroupMemberships.class); if ((groupCredentialSet != null) && (groupCredentialSet.size() == 1)) @@ -1033,13 +1085,9 @@ public class GMSClient return false; } - Set<Principal> subjectPrincipals = subject.getPrincipals(); - Iterator<Principal> i = subjectPrincipals.iterator(); - Principal next = null; - while (i.hasNext()) + for (Principal subjectPrincipal : subject.getPrincipals()) { - next = i.next(); - if (next.equals(userID)) + if (subjectPrincipal.equals(userID)) { return true; }