diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java index b2730be04082c9d4c9fb0bc56516e53ee677c748..380961d8e65420b6822e7f689e6d39829bd6bcdd 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/UserPersistence.java @@ -73,10 +73,7 @@ import java.security.Principal; import java.util.Collection; import java.util.Map; -import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.ac.UserAlreadyExistsException; -import ca.nrc.cadc.ac.UserNotFoundException; -import ca.nrc.cadc.ac.UserRequest; +import ca.nrc.cadc.ac.*; import ca.nrc.cadc.net.TransientException; import com.unboundid.ldap.sdk.DN; @@ -91,7 +88,7 @@ public interface UserPersistence<T extends Principal> * @throws TransientException If an temporary, unexpected problem occurred. * @throws AccessControlException If the operation is not permitted. */ - Map<String, String> getUsers() + Map<String, PersonalDetails> getUsers() throws TransientException, AccessControlException; /** 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 6d272ca3bd2f341a450b90e8c3027a6acc5ce5e0..b133948eb0ef553f34d805512682f339d7b4cce4 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 @@ -488,15 +488,16 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO * @return A map of string keys to string values. * @throws TransientException If an temporary, unexpected problem occurred. */ - public Map<String, String> getUsers() + public Map<String, PersonalDetails> getUsers() throws TransientException { - final Map<String, String> users = new HashMap<String, String>(); + final Map<String, PersonalDetails> users = + new HashMap<String, PersonalDetails>(); try { - final Filter filter = Filter.createPresenceFilter(LDAP_COMMON_NAME); - final String[] attributes = new String[]{LDAP_COMMON_NAME, + final Filter filter = Filter.createPresenceFilter(LDAP_UID); + final String[] attributes = new String[]{LDAP_UID, LDAP_FIRST_NAME, LDAP_LAST_NAME, LDAP_NSACCOUNTLOCK}; @@ -514,10 +515,16 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO { if (!next.hasAttribute(LDAP_NSACCOUNTLOCK)) { - users.put(next.getAttributeValue(LDAP_COMMON_NAME), - next.getAttributeValue(LDAP_FIRST_NAME) - + " " - + next.getAttributeValue(LDAP_LAST_NAME)); + final String trimmedFirstName = + next.getAttributeValue(LDAP_FIRST_NAME).trim(); + final String trimmedLastName = + next.getAttributeValue(LDAP_LAST_NAME).trim(); + final String trimmedUID = + next.getAttributeValue(LDAP_UID).trim(); + + users.put(trimmedUID, + new PersonalDetails(trimmedFirstName, + trimmedLastName)); } } } diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java index 452598a10ea4271fba4fdd547cce682606ae80de..af7662460e342b8ae107af471b1b57bf0aa8d019 100755 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserPersistence.java @@ -68,10 +68,7 @@ */ package ca.nrc.cadc.ac.server.ldap; -import ca.nrc.cadc.ac.User; -import ca.nrc.cadc.ac.UserAlreadyExistsException; -import ca.nrc.cadc.ac.UserNotFoundException; -import ca.nrc.cadc.ac.UserRequest; +import ca.nrc.cadc.ac.*; import ca.nrc.cadc.ac.server.UserPersistence; import ca.nrc.cadc.net.TransientException; import com.unboundid.ldap.sdk.DN; @@ -100,7 +97,7 @@ public class LdapUserPersistence<T extends Principal> } } - public Map<String, String> getUsers() + public Map<String, PersonalDetails> getUsers() throws TransientException, AccessControlException { LdapUserDAO<T> userDAO = null; diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java index 60f75a3ec9c9d070814702c3686250d8c2998c27..ce700e167925a31028583447fb0936f5b264962f 100644 --- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java +++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java @@ -80,6 +80,7 @@ import java.util.Map; import javax.security.auth.Subject; import javax.servlet.http.HttpServletResponse; +import ca.nrc.cadc.ac.PersonalDetails; import ca.nrc.cadc.ac.User; import ca.nrc.cadc.ac.UserRequest; import org.apache.log4j.Logger; @@ -278,7 +279,7 @@ public abstract class UsersAction * * @param users The Map of user IDs to names. */ - protected final void writeUsers(final Map<String, String> users) + protected final void writeUsers(final Map<String, PersonalDetails> users) throws IOException { response.setContentType(acceptedContentType); diff --git a/projects/cadcAccessControl-Server/test/LdapConfig.test.properties b/projects/cadcAccessControl-Server/test/LdapConfig.test.properties index bb88d99c07772fa45762f109b6c5c32be22651f0..0612a33b3987c7efbc6f19c01fb1b4d72efec14a 100644 --- a/projects/cadcAccessControl-Server/test/LdapConfig.test.properties +++ b/projects/cadcAccessControl-Server/test/LdapConfig.test.properties @@ -1,6 +1,11 @@ # This are the configuration fields required by the Ldap ldap-dao unit tests +# Tests are more accurate running on Port 636. If it fails due to SSL/Security +# issues, then make very sure the ca.crt (gimli2.cadc.dao.nrc.ca:~miscsw/ca.crt) +# is installed in your Java Keystore: +# scp gimli2.cadc.dao.nrc.ca:~miscsw/ca.crt /tmp/ca.crt +# ${JAVA_HOME}/bin/keytool -importcert -keystore ${JAVA_HOME}/jre/lib/security/cacerts -file /tmp/ca.crt server = proc5-03.cadc.dao.nrc.ca -port = 389 +port = 636 proxyUser = testproxy usersDn = ou=Users,ou=ds,dc=testcanfar userRequestsDN = ou=UserRequests,ou=ds,dc=testcanfar 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 fda75b28b116d178a44c4af5bff0f14e15c0cc22..fb0f533262766806509a6e4eab41606d4213945d 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 @@ -102,10 +102,10 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest static String testUserDN; static User<X500Principal> testUser; static LdapConfig config; - + @BeforeClass public static void setUpBeforeClass() - throws Exception + throws Exception { Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG); @@ -123,12 +123,12 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest { return new LdapUserDAO(config); } - + String getUserID() { return "CadcDaoTestUser-" + System.currentTimeMillis(); } - + /** * Test of addUser method, of class LdapUserDAO. */ @@ -148,7 +148,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest User<HttpPrincipal> actual = getUserDAO().addUser(userRequest); check(expected, actual); } - + /** * Test of getUser method, of class LdapUserDAO. */ @@ -167,6 +167,8 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest try { User<X500Principal> actual = getUserDAO().getUser(testUser.getUserID()); + User<X500Principal> actual = getUserDAO() + .getUser(testUser.getUserID()); check(testUser, actual); return null; @@ -195,19 +197,31 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest public Object run() throws Exception { try - { + { Collection<DN> groups = getUserDAO().getUserGroups(testUser.getUserID(), false); assertNotNull(groups); assertTrue(!groups.isEmpty()); + Collection<DN> groups = + getUserDAO().getUserGroups(testUser.getUserID(), + false); + assertNotNull("Groups should not be null.", groups); + for (DN groupDN : groups) + { log.debug(groupDN); - + } + groups = getUserDAO().getUserGroups(testUser.getUserID(), true); assertNotNull(groups); assertTrue(!groups.isEmpty()); + groups = getUserDAO().getUserGroups(testUser.getUserID(), + true); + assertNotNull("Groups should not be null.", groups); for (DN groupDN : groups) + { log.debug(groupDN); - + } + return null; } catch (Exception e) @@ -217,7 +231,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest } }); } - + /** * Test of getUserGroups method, of class LdapUserDAO. */ @@ -233,14 +247,22 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest public Object run() throws Exception { try - { + { boolean isMember = getUserDAO().isMember(testUser.getUserID(), "foo"); assertFalse(isMember); - + boolean isMember = + getUserDAO().isMember(testUser.getUserID(), "foo"); + assertFalse("Membership should not exist.", isMember); + String groupDN = "cn=cadcdaotestgroup1," + config.getGroupsDN(); isMember = getUserDAO().isMember(testUser.getUserID(), groupDN); assertTrue(isMember); - + String groupDN = "cn=cadcdaotestgroup1," + + config.getGroupsDN(); + isMember = getUserDAO().isMember(testUser.getUserID(), + groupDN); + assertTrue("Membership should exist.", isMember); + return null; } catch (Exception e) @@ -250,7 +272,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest } }); } - + /** * Test of getMember. */ @@ -266,8 +288,10 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest public Object run() throws Exception { try - { + { User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN)); + User<X500Principal> actual = getUserDAO() + .getMember(new DN(testUserDN)); check(testUser, actual); return null; } @@ -277,7 +301,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest } } }); - + // should also work as a different user subject = new Subject(); subject.getPrincipals().add(new HttpPrincipal("CadcDaoTest2")); @@ -290,7 +314,12 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest { try { +<<<<<<< HEAD User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN)); +======= + User<X500Principal> actual = getUserDAO() + .getMember(new DN(testUserDN)); +>>>>>>> 57bf534cb0ace85be32da449ca35a73e96a379b2 check(testUser, actual); return null; } @@ -301,7 +330,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest } }); } - + /** * Test of testGetCadcUserIDs. */ @@ -309,36 +338,40 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest public void testGetCadcUserIDs() throws Exception { Subject subject = new Subject(); - - + + // anonymous access int users1 = (Integer)Subject.doAs(subject, new PrivilegedExceptionAction<Object>() - { - public Object run() throws Exception - { - try - { - - int count = getUserDAO().getCadcIDs().size(); - assertTrue(count > 0); - return count; - } - catch (Exception e) + int users1 = (Integer) Subject + .doAs(subject, new PrivilegedExceptionAction<Object>() { - throw new Exception("Problems", e); - } - } - }); - + public Object run() throws Exception + { + try + { + + int count = getUserDAO().getCadcIDs().size(); + assertTrue(count > 0); + return count; + } + catch (Exception e) + { + throw new Exception("Problems", e); + } + } + }); + // authenticated access subject.getPrincipals().add(testUser.getUserID()); int users2 = (Integer)Subject.doAs(subject, new PrivilegedExceptionAction<Object>() + int users2 = (Integer) Subject + .doAs(subject, new PrivilegedExceptionAction<Object>() { public Object run() throws Exception { try - { - + { + int count = getUserDAO().getCadcIDs().size(); assertTrue(count > 0); return count; @@ -350,6 +383,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest } }); assertEquals("User listing should be independent of the access type", +<<<<<<< HEAD users1, users2); } @@ -562,26 +596,29 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest }); assertNotNull(updatedUser); check(testUser2, updatedUser); +======= + users1, users2); +>>>>>>> 57bf534cb0ace85be32da449ca35a73e96a379b2 } - + private static void check(final User<? extends Principal> user1, final User<? extends Principal> user2) { assertEquals(user1, user2); assertEquals(user1.details, user2.details); assertEquals(user1.details.size(), user2.details.size()); assertEquals(user1.getIdentities(), user2.getIdentities()); - for(UserDetails d1 : user1.details) + for (UserDetails d1 : user1.details) { assertTrue(user2.details.contains(d1)); - if(d1 instanceof PersonalDetails) + if (d1 instanceof PersonalDetails) { - PersonalDetails pd1 = (PersonalDetails)d1; + PersonalDetails pd1 = (PersonalDetails) d1; boolean found = false; - for(UserDetails d2 : user2.details) + for (UserDetails d2 : user2.details) { - if(d2 instanceof PersonalDetails) + if (d2 instanceof PersonalDetails) { - PersonalDetails pd2 = (PersonalDetails)d2; + PersonalDetails pd2 = (PersonalDetails) d2; assertEquals(pd1, pd2); // already done in contains above but just in case assertEquals(pd1.address, pd2.address); assertEquals(pd1.city, pd2.city); @@ -612,7 +649,7 @@ public class LdapUserDAOTest<T extends Principal> extends AbstractLdapDAOTest } } } - + } - + } diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java index e8f7004fda1c3462fdf4b82e616b2227df24bc00..f1dc7a29aeb521d739a16325c62b97af3a83510a 100644 --- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java +++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java @@ -69,6 +69,7 @@ package ca.nrc.cadc.ac.server.web.users; +import ca.nrc.cadc.ac.PersonalDetails; import ca.nrc.cadc.ac.server.UserPersistence; import ca.nrc.cadc.auth.HttpPrincipal; import org.apache.log4j.Level; @@ -112,11 +113,13 @@ public class GetUsersActionTest createMock(HttpServletResponse.class); final UserPersistence<HttpPrincipal> mockUserPersistence = createMock(UserPersistence.class); - final Map<String, String> userEntries = new HashMap<String, String>(); + final Map<String, PersonalDetails> userEntries = + new HashMap<String, PersonalDetails>(); for (int i = 1; i <= 5; i++) { - userEntries.put("USER_" + i, "USER " + i); + userEntries.put("USER_" + i, + new PersonalDetails("USER", Integer.toString(i))); } final GetUsersAction testSubject = new GetUsersAction(null) @@ -143,7 +146,7 @@ public class GetUsersActionTest testSubject.doAction(null, mockResponse); final JSONArray expected = - new JSONArray("[{\"id\":\"USER_1\",\"name\":\"USER 1\"},{\"id\":\"USER_3\",\"name\":\"USER 3\"},{\"id\":\"USER_2\",\"name\":\"USER 2\"},{\"id\":\"USER_4\",\"name\":\"USER 4\"},{\"id\":\"USER_5\",\"name\":\"USER 5\"}]"); + new JSONArray("[{\"id\":\"USER_1\",\"firstName\":\"USER\",\"lastName\":\"1\"},{\"id\":\"USER_3\",\"firstName\":\"USER\",\"lastName\":\"3\"},{\"id\":\"USER_2\",\"firstName\":\"USER\",\"lastName\":\"2\"},{\"id\":\"USER_4\",\"firstName\":\"USER\",\"lastName\":\"4\"},{\"id\":\"USER_5\",\"firstName\":\"USER\",\"lastName\":\"5\"}]"); final JSONArray result = new JSONArray(writer.toString()); JSONAssert.assertEquals(expected, result, true); @@ -158,11 +161,13 @@ public class GetUsersActionTest createMock(HttpServletResponse.class); final UserPersistence<HttpPrincipal> mockUserPersistence = createMock(UserPersistence.class); - final Map<String, String> userEntries = new HashMap<String, String>(); + final Map<String, PersonalDetails> userEntries = + new HashMap<String, PersonalDetails>(); for (int i = 1; i <= 5; i++) { - userEntries.put("USER_" + i, "USER " + i); + userEntries.put("USER_" + i, + new PersonalDetails("USER", Integer.toString(i))); } final GetUsersAction testSubject = new GetUsersAction(null) @@ -189,19 +194,24 @@ public class GetUsersActionTest final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" + "<users>\r\n" + " <user id=\"USER_1\">\r\n" + - " <name>USER 1</name>\r\n" + + " <firstName>USER</firstName>\r\n" + + " <lastName>1</lastName>\r\n" + " </user>\r\n" + " <user id=\"USER_3\">\r\n" + - " <name>USER 3</name>\r\n" + + " <firstName>USER</firstName>\r\n" + + " <lastName>3</lastName>\r\n" + " </user>\r\n" + " <user id=\"USER_2\">\r\n" + - " <name>USER 2</name>\r\n" + + " <firstName>USER</firstName>\r\n" + + " <lastName>2</lastName>\r\n" + " </user>\r\n" + " <user id=\"USER_4\">\r\n" + - " <name>USER 4</name>\r\n" + + " <firstName>USER</firstName>\r\n" + + " <lastName>4</lastName>\r\n" + " </user>\r\n" + " <user id=\"USER_5\">\r\n" + - " <name>USER 5</name>\r\n" + + " <firstName>USER</firstName>\r\n" + + " <lastName>5</lastName>\r\n" + " </user>\r\n" + "</users>\r\n"; final String result = writer.toString(); 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 159e48eaddffdb4f62b6fea3a1eea8af3b29fc33..8f4eab02036b1155294d80566c74a434875ad142 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java @@ -68,12 +68,7 @@ */ 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.io.*; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; @@ -93,16 +88,14 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import javax.security.auth.Subject; +import ca.nrc.cadc.ac.*; +import ca.nrc.cadc.auth.HttpPrincipal; +import ca.nrc.cadc.util.StringUtil; import org.apache.log4j.Logger; -import ca.nrc.cadc.ac.Group; -import ca.nrc.cadc.ac.GroupAlreadyExistsException; -import ca.nrc.cadc.ac.GroupNotFoundException; import ca.nrc.cadc.ac.xml.GroupReader; import ca.nrc.cadc.ac.xml.GroupWriter; import ca.nrc.cadc.ac.xml.GroupsReader; -import ca.nrc.cadc.ac.Role; -import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.auth.AuthenticationUtil; import ca.nrc.cadc.auth.SSLUtil; import ca.nrc.cadc.net.HttpDownload; @@ -110,6 +103,7 @@ import ca.nrc.cadc.net.HttpPost; import ca.nrc.cadc.net.HttpUpload; import ca.nrc.cadc.net.InputStreamWrapper; import ca.nrc.cadc.net.NetUtil; +import org.json.JSONObject; /** @@ -119,21 +113,21 @@ import ca.nrc.cadc.net.NetUtil; public class GMSClient { private static final Logger log = Logger.getLogger(GMSClient.class); - + // socket factory to use when connecting private SSLSocketFactory sslSocketFactory; private SSLSocketFactory mySocketFactory; - + private String baseURL; /** * Constructor. - * + * * @param baseURL The URL of the supporting access control web service - * obtained from the registry. + * obtained from the registry. */ - public GMSClient(String baseURL) - throws IllegalArgumentException + public GMSClient(final String baseURL) + throws IllegalArgumentException { if (baseURL == null) { @@ -145,7 +139,7 @@ public class GMSClient } catch (MalformedURLException e) { - throw new IllegalArgumentException("URL is malformed: " + + throw new IllegalArgumentException("URL is malformed: " + e.getMessage()); } @@ -169,6 +163,63 @@ public class GMSClient throw new UnsupportedOperationException("Not yet implemented"); } + + + /** + * Obtain all of the users as userID - name in JSON format. + * + * @return List of HTTP Principal users. + * @throws IOException Any errors in reading. + */ + public List<User<HttpPrincipal>> getDisplayUsers() throws IOException + { + final List<User<HttpPrincipal>> webUsers = + new ArrayList<User<HttpPrincipal>>(); + + final HttpDownload httpDownload = + createDisplayUsersHTTPDownload(webUsers); + + httpDownload.setRequestProperty("Accept", "application/json"); + httpDownload.run(); + + final Throwable error = httpDownload.getThrowable(); + + if (error != null) + { + final String errMessage = error.getMessage(); + final int responseCode = httpDownload.getResponseCode(); + + log.debug("getDisplayUsers response " + responseCode + ": " + + errMessage); + + if ((responseCode == 401) || (responseCode == 403) || + (responseCode == -1)) + { + throw new AccessControlException(errMessage); + } + else 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 webUsers; + } + + HttpDownload createDisplayUsersHTTPDownload( + final List<User<HttpPrincipal>> webUsers) throws IOException + { + final URL usersListURL = new URL(this.baseURL + "/users"); + return new HttpDownload(usersListURL, + new JSONUserListInputStreamWrapper(webUsers)); + } + /** * Create a new group. * @@ -176,17 +227,17 @@ public class GMSClient * @return The newly created group will all the information. * @throws GroupAlreadyExistsException If a group with the same name already * exists. - * @throws AccessControlException If unauthorized to perform this operation. + * @throws AccessControlException If unauthorized to perform this operation. * @throws UserNotFoundException * @throws IOException */ public Group createGroup(Group group) - throws GroupAlreadyExistsException, AccessControlException, - UserNotFoundException, IOException + throws GroupAlreadyExistsException, AccessControlException, + UserNotFoundException, IOException { URL createGroupURL = new URL(this.baseURL + "/groups"); log.debug("createGroupURL request to " + createGroupURL.toString()); - + // reset the state of the cache clearCache(); @@ -207,8 +258,8 @@ public class GMSClient { log.debug("createGroup throwable", error); // transfer returns a -1 code for anonymous uploads. - if ((transfer.getResponseCode() == -1) || - (transfer.getResponseCode() == 401) || + if ((transfer.getResponseCode() == -1) || + (transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403)) { throw new AccessControlException(error.getMessage()); @@ -251,7 +302,7 @@ public class GMSClient * @throws java.io.IOException */ public Group getGroup(String groupName) - throws GroupNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, AccessControlException, IOException { URL getGroupURL = new URL(this.baseURL + "/groups/" + groupName); log.debug("getGroup request to " + getGroupURL.toString()); @@ -264,10 +315,11 @@ public class GMSClient Throwable error = transfer.getThrowable(); if (error != null) { - log.debug("getGroup throwable (" + transfer.getResponseCode() + ")", error); + log.debug("getGroup throwable (" + transfer + .getResponseCode() + ")", error); // transfer returns a -1 code for anonymous access. - if ((transfer.getResponseCode() == -1) || - (transfer.getResponseCode() == 401) || + if ((transfer.getResponseCode() == -1) || + (transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403)) { throw new AccessControlException(error.getMessage()); @@ -295,7 +347,7 @@ public class GMSClient throw new RuntimeException(bug); } } - + /** * Get the all group names. * @@ -304,7 +356,7 @@ public class GMSClient * @throws java.io.IOException */ public List<String> getGroupNames() - throws AccessControlException, IOException + throws AccessControlException, IOException { final URL getGroupNamesURL = new URL(this.baseURL + "/groups"); log.debug("getGroupNames request to " + getGroupNamesURL.toString()); @@ -312,26 +364,28 @@ public class GMSClient 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); + @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); + } } - } - catch (Exception bug) - { - log.error("Unexpected exception", bug); - throw new RuntimeException(bug); - } - } - }); + }); httpDownload.setSSLSocketFactory(getSSLSocketFactory()); httpDownload.run(); @@ -346,8 +400,8 @@ public class GMSClient log.debug("getGroupNames response " + responseCode + ": " + errMessage); - if ((responseCode == 401) || (responseCode == 403) || - (responseCode == -1)) + if ((responseCode == 401) || (responseCode == 403) || + (responseCode == -1)) { throw new AccessControlException(errMessage); } @@ -370,36 +424,37 @@ public class GMSClient * @param group The update group object. * @return The group after update. * @throws IllegalArgumentException If cyclical membership is detected. - * @throws GroupNotFoundException If the group was not found. - * @throws UserNotFoundException If a member was not found. - * @throws AccessControlException If unauthorized to perform this operation. + * @throws GroupNotFoundException If the group 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) - throws IllegalArgumentException, GroupNotFoundException, UserNotFoundException, - AccessControlException, IOException + throws IllegalArgumentException, GroupNotFoundException, + UserNotFoundException, + AccessControlException, IOException { URL updateGroupURL = new URL(this.baseURL + "/groups/" + group.getID()); log.debug("updateGroup request to " + updateGroupURL.toString()); - + // reset the state of the cache clearCache(); StringBuilder groupXML = new StringBuilder(); GroupWriter.write(group, groupXML); log.debug("updateGroup: " + groupXML); - - HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(), + + HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(), "application/xml", true); transfer.setSSLSocketFactory(getSSLSocketFactory()); transfer.run(); - + Throwable error = transfer.getThrowable(); if (error != null) { // transfer returns a -1 code for anonymous access. - if ((transfer.getResponseCode() == -1) || - (transfer.getResponseCode() == 401) || + if ((transfer.getResponseCode() == -1) || + (transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403)) { throw new AccessControlException(error.getMessage()); @@ -410,14 +465,19 @@ public class GMSClient } if (transfer.getResponseCode() == 404) { - if (error.getMessage() != null && error.getMessage().toLowerCase().contains("user")) + if (error.getMessage() != null && error.getMessage() + .toLowerCase().contains("user")) + { throw new UserNotFoundException(error.getMessage()); + } else + { throw new GroupNotFoundException(error.getMessage()); + } } throw new IOException(error); } - + try { String retXML = transfer.getResponseBody(); @@ -440,15 +500,15 @@ public class GMSClient * @throws java.io.IOException */ public void deleteGroup(String groupName) - throws GroupNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, AccessControlException, IOException { URL deleteGroupURL = new URL(this.baseURL + "/groups/" + groupName); log.debug("deleteGroup request to " + deleteGroupURL.toString()); - + // reset the state of the cache clearCache(); - HttpURLConnection conn = + HttpURLConnection conn = (HttpURLConnection) deleteGroupURL.openConnection(); conn.setRequestMethod("DELETE"); @@ -465,19 +525,19 @@ public class GMSClient { responseCode = conn.getResponseCode(); } - catch(Exception e) + catch (Exception e) { throw new AccessControlException(e.getMessage()); } - + if (responseCode != 200) { String errMessage = NetUtil.getErrorBody(conn); - log.debug("deleteGroup response " + responseCode + ": " + + log.debug("deleteGroup response " + responseCode + ": " + errMessage); - if ((responseCode == 401) || (responseCode == 403) || - (responseCode == -1)) + if ((responseCode == 401) || (responseCode == 403) || + (responseCode == -1)) { throw new AccessControlException(errMessage); } @@ -499,19 +559,19 @@ public class GMSClient * @param targetGroupName The group in which to add the group member. * @param groupMemberName The group member to add. * @throws IllegalArgumentException If cyclical membership is detected. - * @throws GroupNotFoundException If the group was not found. - * @throws AccessControlException If unauthorized to perform this operation. + * @throws GroupNotFoundException If the group was not found. + * @throws AccessControlException If unauthorized to perform this operation. * @throws java.io.IOException */ public void addGroupMember(String targetGroupName, String groupMemberName) - throws IllegalArgumentException, GroupNotFoundException, - AccessControlException, IOException + throws IllegalArgumentException, GroupNotFoundException, + AccessControlException, IOException { - URL addGroupMemberURL = new URL(this.baseURL + "/groups/" + - targetGroupName + "/groupMembers/" + + URL addGroupMemberURL = new URL(this.baseURL + "/groups/" + + targetGroupName + "/groupMembers/" + groupMemberName); log.debug("addGroupMember request to " + addGroupMemberURL.toString()); - + // reset the state of the cache clearCache(); @@ -527,8 +587,8 @@ public class GMSClient final int responseCode = httpUpload.getResponseCode(); final String errMessage = error.getMessage(); - if ((responseCode == -1) || - (responseCode == 401) || + if ((responseCode == -1) || + (responseCode == 401) || (responseCode == 403)) { throw new AccessControlException(errMessage); @@ -549,23 +609,24 @@ public class GMSClient * Add a user as a member of a group. * * @param targetGroupName The group in which to add the group member. - * @param userID The user to add. + * @param userID The user to add. * @throws GroupNotFoundException If the group was not found. - * @throws UserNotFoundException 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. */ public void addUserMember(String targetGroupName, Principal userID) - throws GroupNotFoundException, UserNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, UserNotFoundException, + AccessControlException, IOException { String userIDType = AuthenticationUtil.getPrincipalType(userID); String encodedUserID = URLEncoder.encode(userID.getName(), "UTF-8"); - URL addUserMemberURL = new URL(this.baseURL + "/groups/" + - targetGroupName + "/userMembers/" + + URL addUserMemberURL = new URL(this.baseURL + "/groups/" + + targetGroupName + "/userMembers/" + encodedUserID + "?idType=" + userIDType); log.debug("addUserMember request to " + addUserMemberURL.toString()); - + // reset the state of the cache clearCache(); @@ -581,8 +642,8 @@ public class GMSClient final int responseCode = httpUpload.getResponseCode(); final String errMessage = error.getMessage(); - if ((responseCode == -1) || - (responseCode == 401) || + if ((responseCode == -1) || + (responseCode == 401) || (responseCode == 403)) { throw new AccessControlException(errMessage); @@ -593,10 +654,15 @@ public class GMSClient } if (responseCode == 404) { - if (errMessage != null && errMessage.toLowerCase().contains("user")) + if (errMessage != null && errMessage.toLowerCase() + .contains("user")) + { throw new UserNotFoundException(errMessage); + } else + { throw new GroupNotFoundException(errMessage); + } } throw new IOException(errMessage); } @@ -611,20 +677,20 @@ public class GMSClient * @throws java.io.IOException * @throws AccessControlException If unauthorized to perform this operation. */ - public void removeGroupMember(String targetGroupName, + public void removeGroupMember(String targetGroupName, String groupMemberName) - throws GroupNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, AccessControlException, IOException { - URL removeGroupMemberURL = new URL(this.baseURL + "/groups/" + - targetGroupName + "/groupMembers/" + + URL removeGroupMemberURL = new URL(this.baseURL + "/groups/" + + targetGroupName + "/groupMembers/" + groupMemberName); - log.debug("removeGroupMember request to " + + log.debug("removeGroupMember request to " + removeGroupMemberURL.toString()); - + // reset the state of the cache clearCache(); - HttpURLConnection conn = + HttpURLConnection conn = (HttpURLConnection) removeGroupMemberURL.openConnection(); conn.setRequestMethod("DELETE"); @@ -634,23 +700,25 @@ public class GMSClient ((HttpsURLConnection) conn) .setSSLSocketFactory(getSSLSocketFactory()); } - + // Try to handle anonymous access and throw AccessControlException int responseCode = -1; try { responseCode = conn.getResponseCode(); } - catch (Exception ignore) {} - + catch (Exception ignore) + { + } + if (responseCode != 200) { String errMessage = NetUtil.getErrorBody(conn); - log.debug("removeGroupMember response " + responseCode + ": " + + log.debug("removeGroupMember response " + responseCode + ": " + errMessage); - if ((responseCode == -1) || - (responseCode == 401) || + if ((responseCode == -1) || + (responseCode == 401) || (responseCode == 403)) { throw new AccessControlException(errMessage); @@ -671,29 +739,30 @@ public class GMSClient * Remove a user as a member of a group. * * @param targetGroupName The group from which to remove the group member. - * @param userID The user to remove. + * @param userID The user to remove. * @throws GroupNotFoundException If the group was not found. - * @throws UserNotFoundException 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. */ public void removeUserMember(String targetGroupName, Principal userID) - throws GroupNotFoundException, UserNotFoundException, AccessControlException, IOException + throws GroupNotFoundException, UserNotFoundException, + AccessControlException, IOException { String userIDType = AuthenticationUtil.getPrincipalType(userID); String encodedUserID = URLEncoder.encode(userID.toString(), "UTF-8"); - URL removeUserMemberURL = new URL(this.baseURL + "/groups/" + - targetGroupName + "/userMembers/" + - encodedUserID + "?idType=" + + URL removeUserMemberURL = new URL(this.baseURL + "/groups/" + + targetGroupName + "/userMembers/" + + encodedUserID + "?idType=" + userIDType); log.debug("removeUserMember request to " + removeUserMemberURL.toString()); - + // reset the state of the cache clearCache(); - HttpURLConnection conn = + HttpURLConnection conn = (HttpURLConnection) removeUserMemberURL.openConnection(); conn.setRequestMethod("DELETE"); @@ -703,23 +772,25 @@ public class GMSClient ((HttpsURLConnection) conn) .setSSLSocketFactory(getSSLSocketFactory()); } - + // Try to handle anonymous access and throw AccessControlException int responseCode = -1; try { responseCode = conn.getResponseCode(); } - catch (Exception ignore) {} + catch (Exception ignore) + { + } if (responseCode != 200) { String errMessage = NetUtil.getErrorBody(conn); - log.debug("removeUserMember response " + responseCode + ": " + + log.debug("removeUserMember response " + responseCode + ": " + errMessage); - if ((responseCode == -1) || - (responseCode == 401) || + if ((responseCode == -1) || + (responseCode == 401) || (responseCode == 403)) { throw new AccessControlException(errMessage); @@ -730,10 +801,15 @@ public class GMSClient } if (responseCode == 404) { - if (errMessage != null && errMessage.toLowerCase().contains("user")) + if (errMessage != null && errMessage.toLowerCase() + .contains("user")) + { throw new UserNotFoundException(errMessage); + } else + { throw new GroupNotFoundException(errMessage); + } } throw new IOException(errMessage); } @@ -741,42 +817,42 @@ public class GMSClient /** * Get all the memberships of the user of a certain role. - * + * * @param userID Identifies the user. - * @param role The role to look up. + * @param role The role to look up. * @return A list of groups for which the user has the role. - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public List<Group> getMemberships(Principal userID, Role role) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { if (userID == null || role == null) { throw new IllegalArgumentException("userID and role are required."); } - + List<Group> cachedGroups = getCachedGroups(userID, role); if (cachedGroups != null) { return cachedGroups; } - + String idType = AuthenticationUtil.getPrincipalType(userID); String id = userID.getName(); String roleString = role.getValue(); - + StringBuilder searchGroupURL = new StringBuilder(this.baseURL); searchGroupURL.append("/search?"); - + 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(); URL url = new URL(searchGroupURL.toString()); @@ -790,8 +866,8 @@ public class GMSClient { log.debug("getMemberships throwable", error); // transfer returns a -1 code for anonymous access. - if ((transfer.getResponseCode() == -1) || - (transfer.getResponseCode() == 401) || + if ((transfer.getResponseCode() == -1) || + (transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403)) { throw new AccessControlException(error.getMessage()); @@ -821,50 +897,50 @@ public class GMSClient throw new RuntimeException(bug); } } - + /** * Return the group, specified by paramter groupName, if the user, * identified by userID, is a member of that group. Return null * otherwise. - * + * <p/> * This call is identical to getMemberShip(userID, groupName, Role.MEMBER) - * - * @param userID Identifies the user. + * + * @param userID Identifies the user. * @param groupName Identifies the group. * @return The group or null of the user is not a member. - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public Group getMembership(Principal userID, String groupName) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { return getMembership(userID, groupName, Role.MEMBER); } - + /** * Return the group, specified by paramter groupName, if the user, * identified by userID, is a member (of type role) of that group. * Return null otherwise. - * - * @param userID Identifies the user. + * + * @param userID Identifies the user. * @param groupName Identifies the group. - * @param role The membership role to search. + * @param role The membership role to search. * @return The group or null of the user is not a member. - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public Group getMembership(Principal userID, String groupName, Role role) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { if (userID == null || groupName == null || role == null) { throw new IllegalArgumentException("userID and role are required."); } - + List<Group> cachedGroups = getCachedGroups(userID, role); if (cachedGroups != null) { @@ -878,11 +954,11 @@ public class GMSClient return null; } } - + String idType = AuthenticationUtil.getPrincipalType(userID); String id = userID.getName(); String roleString = role.getValue(); - + StringBuilder searchGroupURL = new StringBuilder(this.baseURL); searchGroupURL.append("/search?"); @@ -893,7 +969,7 @@ public class GMSClient .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(); URL url = new URL(searchGroupURL.toString()); @@ -907,8 +983,8 @@ public class GMSClient { log.debug("getMembership throwable", error); // transfer returns a -1 code for anonymous access. - if ((transfer.getResponseCode() == -1) || - (transfer.getResponseCode() == 401) || + if ((transfer.getResponseCode() == -1) || + (transfer.getResponseCode() == 401) || (transfer.getResponseCode() == 403)) { throw new AccessControlException(error.getMessage()); @@ -948,40 +1024,40 @@ public class GMSClient throw new RuntimeException(bug); } } - + /** * Check if userID is a member of groupName. - * + * <p/> * This is equivalent to isMember(userID, groupName, Role.MEMBER) - * - * @param userID Identifies the user. + * + * @param userID Identifies the user. * @param groupName Identifies the group. * @return True if the user is a member of the group - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public boolean isMember(Principal userID, String groupName) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { return isMember(userID, groupName, Role.MEMBER); } - + /** * Check if userID is a member (of type role) of groupName. - * - * @param userID Identifies the user. + * + * @param userID Identifies the user. * @param groupName Identifies the group. - * @param role The type of membership. + * @param role The type of membership. * @return True if the user is a member of the group - * @throws UserNotFoundException If the user does not exist. - * @throws AccessControlException If not allowed to peform the search. + * @throws UserNotFoundException If the user does not exist. + * @throws AccessControlException If not allowed to peform the search. * @throws IllegalArgumentException If a parameter is null. - * @throws IOException If an unknown error occured. + * @throws IOException If an unknown error occured. */ public boolean isMember(Principal userID, String groupName, Role role) - throws UserNotFoundException, AccessControlException, IOException + throws UserNotFoundException, AccessControlException, IOException { Group group = getMembership(userID, groupName, role); return group != null; @@ -993,24 +1069,27 @@ 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"); + + "cannot set SSLSocketFactory after using one created from Subject"); + } this.sslSocketFactory = sslSocketFactory; clearCache(); } - + private int subjectHashCode = 0; + private SSLSocketFactory getSSLSocketFactory() { 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) { @@ -1022,18 +1101,21 @@ public class GMSClient { int c = s.hashCode(); if (c != subjectHashCode) - throw new IllegalStateException("Illegal use of " - + this.getClass().getSimpleName() - + ": subject change not supported for internal SSLSocketFactory"); + { + throw new IllegalStateException("Illegal use of " + + this.getClass() + .getSimpleName() + + ": subject change not supported for internal SSLSocketFactory"); + } } return this.mySocketFactory; } - + protected void clearCache() { AccessControlContext acContext = AccessController.getContext(); Subject subject = Subject.getSubject(acContext); - + if (subject != null) { log.debug("Clearing cache"); @@ -1045,16 +1127,18 @@ public class GMSClient { AccessControlContext acContext = AccessController.getContext(); Subject subject = Subject.getSubject(acContext); - + // only consult cache if the userID is of the calling subject if (userIsSubject(userID, subject)) { - Set groupCredentialSet = subject.getPrivateCredentials(GroupMemberships.class); - if ((groupCredentialSet != null) && + Set groupCredentialSet = subject + .getPrivateCredentials(GroupMemberships.class); + if ((groupCredentialSet != null) && (groupCredentialSet.size() == 1)) { Iterator i = groupCredentialSet.iterator(); - GroupMemberships groupMemberships = ((GroupMemberships) i.next()); + GroupMemberships groupMemberships = ((GroupMemberships) i + .next()); return groupMemberships.memberships.get(role); } } @@ -1065,15 +1149,16 @@ public class GMSClient { AccessControlContext acContext = AccessController.getContext(); Subject subject = Subject.getSubject(acContext); - + // only save to cache if the userID is of the calling subject if (userIsSubject(userID, subject)) { log.debug("Caching groups for " + userID + ", role " + role); - + final GroupMemberships groupCredentials; - Set groupCredentialSet = subject.getPrivateCredentials(GroupMemberships.class); - if ((groupCredentialSet != null) && + Set groupCredentialSet = subject + .getPrivateCredentials(GroupMemberships.class); + if ((groupCredentialSet != null) && (groupCredentialSet.size() == 1)) { Iterator i = groupCredentialSet.iterator(); @@ -1084,18 +1169,18 @@ public class GMSClient groupCredentials = new GroupMemberships(); subject.getPrivateCredentials().add(groupCredentials); } - - groupCredentials.memberships.put(role, groups); + + groupCredentials.memberships.put(role, groups); } } - + protected boolean userIsSubject(Principal userID, Subject subject) { if (userID == null || subject == null) { return false; } - + for (Principal subjectPrincipal : subject.getPrincipals()) { if (subjectPrincipal.equals(userID)) diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java new file mode 100644 index 0000000000000000000000000000000000000000..8e3a47447965467f2f822dc5a532fff1ab0571a0 --- /dev/null +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java @@ -0,0 +1,154 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2015. (c) 2015. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * + ************************************************************************ + */ + +package ca.nrc.cadc.ac.client; + +import ca.nrc.cadc.ac.PersonalDetails; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.auth.HttpPrincipal; +import ca.nrc.cadc.net.InputStreamWrapper; +import ca.nrc.cadc.util.StringUtil; +import org.apache.log4j.Logger; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; + +public class JSONUserListInputStreamWrapper implements InputStreamWrapper +{ + private static final Logger LOGGER = Logger + .getLogger(JSONUserListInputStreamWrapper.class); + private final List<User<HttpPrincipal>> output; + + + public JSONUserListInputStreamWrapper( + final List<User<HttpPrincipal>> output) + { + this.output = output; + } + + + /** + * Read the stream in. + * + * @param inputStream The stream to read from. + * @throws IOException Any reading exceptions. + */ + @Override + public void read(final InputStream inputStream) throws IOException + { + String line = null; + + try + { + final InputStreamReader inReader = + new InputStreamReader(inputStream); + final BufferedReader reader = new BufferedReader(inReader); + + while (StringUtil.hasText(line = reader.readLine())) + { + // Deal with arrays stuff. + while (line.startsWith("[") || line.startsWith(",")) + { + line = line.substring(1); + } + + while (line.endsWith("]") || line.endsWith(",")) + { + line = line.substring(0, (line.length() - 1)); + } + + if (StringUtil.hasText(line)) + { + LOGGER.debug(String.format("Reading: %s", line)); + + final JSONObject jsonObject = new JSONObject(line); + final User<HttpPrincipal> webUser = + new User<HttpPrincipal>( + new HttpPrincipal(jsonObject + .getString("id"))); + final String firstName = jsonObject.getString("firstName"); + final String lastName = jsonObject.getString("lastName"); + + webUser.details + .add(new PersonalDetails(firstName, lastName)); + + output.add(webUser); + } + } + } + catch (Exception bug) + { + throw new IOException(bug + (StringUtil.hasText(line) + ? "Error line is " + line : "")); + } + } +} diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java index 230559285f8a2349351a475015873035b39cc6f7..308148bcdadde1530c994bc1bea70a7a61f9cd14 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java @@ -68,6 +68,7 @@ package ca.nrc.cadc.ac.json; +import ca.nrc.cadc.ac.PersonalDetails; import org.json.JSONException; import org.json.JSONWriter; @@ -81,7 +82,7 @@ import java.util.Map; */ public class UsersWriter { - public static void write(final Map<String, String> users, + public static void write(final Map<String, PersonalDetails> users, final Writer writer) throws IOException { final JSONWriter jsonWriter = new JSONWriter(writer); @@ -90,14 +91,19 @@ public class UsersWriter { jsonWriter.array(); - for (final Map.Entry<String, String> entry : users.entrySet()) + for (final Map.Entry<String, PersonalDetails> entry + : users.entrySet()) { jsonWriter.object(); jsonWriter.key("id").value(entry.getKey()); - jsonWriter.key("name").value(entry.getValue()); + jsonWriter.key("firstName").value(entry.getValue(). + getFirstName()); + jsonWriter.key("lastName").value(entry.getValue(). + getLastName()); jsonWriter.endObject(); + writer.write("\n"); } } catch (JSONException e) @@ -112,7 +118,7 @@ public class UsersWriter } catch (JSONException e) { - throw new IOException(e); + // Do nothing. } } } diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java index f8584a468b14f2b23bef82632017f2192cf9f115..757af4901c0a2572ac563350ca76e142dd7d12ea 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java @@ -68,6 +68,7 @@ package ca.nrc.cadc.ac.xml; +import ca.nrc.cadc.ac.PersonalDetails; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.output.Format; @@ -86,21 +87,25 @@ public class UsersWriter * @param writer The Writer to output to. * @throws IOException Any writing errors. */ - public static void write(final Map<String, String> users, + public static void write(final Map<String, PersonalDetails> users, final Writer writer) throws IOException { // Create the root users Element. final Element usersElement = new Element("users"); - for (final Map.Entry<String, String> entry : users.entrySet()) + for (final Map.Entry<String, PersonalDetails> entry : users.entrySet()) { final Element userEntryElement = new Element("user"); - final Element nameElement = new Element("name"); + final Element firstNameElement = new Element("firstName"); + final Element lastNameElement = new Element("lastName"); userEntryElement.setAttribute("id", entry.getKey()); - nameElement.setText(entry.getValue()); - userEntryElement.addContent(nameElement); + firstNameElement.setText(entry.getValue().getFirstName()); + userEntryElement.addContent(firstNameElement); + + lastNameElement.setText(entry.getValue().getLastName()); + userEntryElement.addContent(lastNameElement); usersElement.addContent(userEntryElement); } diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java index 3025fb37678ac4fc3d0f464e6b320f9ced4ebcaf..2da9b1e8948109ca172b198bf0e96965b54b6a7e 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/GMSClientTest.java @@ -69,6 +69,7 @@ package ca.nrc.cadc.ac.client; +import java.io.IOException; import java.net.URI; import java.net.URL; import java.security.PrivilegedExceptionAction; @@ -77,10 +78,10 @@ import java.util.List; import javax.security.auth.Subject; +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.net.HttpDownload; import org.apache.log4j.Level; import org.apache.log4j.Logger; -import org.junit.Assert; -import org.junit.Test; import ca.nrc.cadc.ac.AC; import ca.nrc.cadc.ac.Group; @@ -89,6 +90,11 @@ import ca.nrc.cadc.auth.HttpPrincipal; import ca.nrc.cadc.reg.client.RegistryClient; import ca.nrc.cadc.util.Log4jInit; +import org.junit.Assert; +import org.junit.Test; +import static org.easymock.EasyMock.*; + + public class GMSClientTest { @@ -98,7 +104,41 @@ public class GMSClientTest { Log4jInit.setLevel("ca.nrc.cadc.ac", Level.DEBUG); } - + + + @Test + public void testGetDisplayUsers() throws Exception + { + final HttpDownload mockHTTPDownload = createMock(HttpDownload.class); + final GMSClient testSubject = new GMSClient("http://mysite.com/users") + { + @Override + HttpDownload createDisplayUsersHTTPDownload( + List<User<HttpPrincipal>> webUsers) throws IOException + { + return mockHTTPDownload; + } + }; + + mockHTTPDownload.setRequestProperty("Accept", "application/json"); + expectLastCall().once(); + + mockHTTPDownload.run(); + expectLastCall().once(); + + expect(mockHTTPDownload.getThrowable()).andReturn(null).once(); + + expect(mockHTTPDownload.getContentLength()).andReturn(88l).once(); + expect(mockHTTPDownload.getContentType()).andReturn( + "application/json").once(); + + replay(mockHTTPDownload); + + testSubject.getDisplayUsers(); + + verify(mockHTTPDownload); + } + @Test public void testUserIsSubject() { diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapperTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapperTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fe977e4e3e5120c17b8e407df7bc210767384099 --- /dev/null +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapperTest.java @@ -0,0 +1,102 @@ +/* + ************************************************************************ + ******************* CANADIAN ASTRONOMY DATA CENTRE ******************* + ************** CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES ************** + * + * (c) 2015. (c) 2015. + * Government of Canada Gouvernement du Canada + * National Research Council Conseil national de recherches + * Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6 + * All rights reserved Tous droits réservés + * + * NRC disclaims any warranties, Le CNRC dénie toute garantie + * expressed, implied, or énoncée, implicite ou légale, + * statutory, of any kind with de quelque nature que ce + * respect to the software, soit, concernant le logiciel, + * including without limitation y compris sans restriction + * any warranty of merchantability toute garantie de valeur + * or fitness for a particular marchande ou de pertinence + * purpose. NRC shall not be pour un usage particulier. + * liable in any event for any Le CNRC ne pourra en aucun cas + * damages, whether direct or être tenu responsable de tout + * indirect, special or general, dommage, direct ou indirect, + * consequential or incidental, particulier ou général, + * arising from the use of the accessoire ou fortuit, résultant + * software. Neither the name de l'utilisation du logiciel. Ni + * of the National Research le nom du Conseil National de + * Council of Canada nor the Recherches du Canada ni les noms + * names of its contributors may de ses participants ne peuvent + * be used to endorse or promote être utilisés pour approuver ou + * products derived from this promouvoir les produits dérivés + * software without specific prior de ce logiciel sans autorisation + * written permission. préalable et particulière + * par écrit. + * + * This file is part of the Ce fichier fait partie du projet + * OpenCADC project. OpenCADC. + * + * OpenCADC is free software: OpenCADC est un logiciel libre ; + * you can redistribute it and/or vous pouvez le redistribuer ou le + * modify it under the terms of modifier suivant les termes de + * the GNU Affero General Public la “GNU Affero General Public + * License as published by the License” telle que publiée + * Free Software Foundation, par la Free Software Foundation + * either version 3 of the : soit la version 3 de cette + * License, or (at your option) licence, soit (à votre gré) + * any later version. toute version ultérieure. + * + * OpenCADC is distributed in the OpenCADC est distribué + * hope that it will be useful, dans l’espoir qu’il vous + * but WITHOUT ANY WARRANTY; sera utile, mais SANS AUCUNE + * without even the implied GARANTIE : sans même la garantie + * warranty of MERCHANTABILITY implicite de COMMERCIALISABILITÉ + * or FITNESS FOR A PARTICULAR ni d’ADÉQUATION À UN OBJECTIF + * PURPOSE. See the GNU Affero PARTICULIER. Consultez la Licence + * General Public License for Générale Publique GNU Affero + * more details. pour plus de détails. + * + * You should have received Vous devriez avoir reçu une + * a copy of the GNU Affero copie de la Licence Générale + * General Public License along Publique GNU Affero avec + * with OpenCADC. If not, see OpenCADC ; si ce n’est + * <http://www.gnu.org/licenses/>. pas le cas, consultez : + * <http://www.gnu.org/licenses/>. + * + * + ************************************************************************ + */ + +package ca.nrc.cadc.ac.client; + +import ca.nrc.cadc.ac.User; +import ca.nrc.cadc.auth.HttpPrincipal; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import org.junit.Test; +import static org.junit.Assert.*; + + +public class JSONUserListInputStreamWrapperTest +{ + @Test + public void readInputStream() throws Exception + { + final List<User<HttpPrincipal>> output = + new ArrayList<User<HttpPrincipal>>(); + final JSONUserListInputStreamWrapper testSubject = + new JSONUserListInputStreamWrapper(output); + final InputStream inputStream = + new ByteArrayInputStream("[{\"id\":\"CADCTest\",\"firstName\":\"CADCtest\",\"lastName\":\"USER\"}\n,{\"id\":\"User_2\",\"firstName\":\"User\",\"lastName\":\"2\"}]".getBytes()); + + testSubject.read(inputStream); + + assertEquals("First item is wrong.", "CADCTest", + output.get(0).getUserID().getName()); + assertEquals("First item is wrong.", "User_2", + output.get(1).getUserID().getName()); + } +}