From 92a215fcc8f8f4bdc01699fae7ad384789becb3e Mon Sep 17 00:00:00 2001 From: Dustin Jenkins <Dustin.Jenkins@nrc-cnrc.gc.ca> Date: Mon, 24 Aug 2015 13:42:06 -0700 Subject: [PATCH] Story 1659: Re-implement missing function. --- .../src/ca/nrc/cadc/ac/client/GMSClient.java | 70 ++++++- .../JSONUserListInputStreamWrapper.java | 154 +++++++++++++++ .../JsonUserListInputStreamWrapper.java | 8 +- .../ca/nrc/cadc/ac/client/GMSClientTest.java | 178 +++++++++++------- .../JsonUserListInputStreamWrapperTest.java | 6 +- 5 files changed, 336 insertions(+), 80 deletions(-) create mode 100644 projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java 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 759e413e..00bb7c81 100755 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java @@ -84,7 +84,6 @@ import java.security.AccessController; import java.security.Principal; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -93,13 +92,10 @@ 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 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.Role; -import ca.nrc.cadc.ac.UserNotFoundException; import ca.nrc.cadc.ac.xml.GroupListReader; import ca.nrc.cadc.ac.xml.GroupReader; import ca.nrc.cadc.ac.xml.GroupWriter; @@ -183,6 +179,68 @@ public class GMSClient implements TransferListener 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); + } + else + { + throw new IOException("HttpResponse (" + responseCode + ") - " + + errMessage); + } + } + + log.debug("Content-Length: " + httpDownload.getContentLength()); + log.debug("Content-Type: " + httpDownload.getContentType()); + + return webUsers; + } + + + /** + * Create a new HTTPDownload instance. Testers can override as needed. + * + * @param webUsers The User objects. + * @return HttpDownload instance. Never null. + * @throws IOException Any writing/reading errors. + */ + 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. * 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 00000000..8e3a4744 --- /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/client/JsonUserListInputStreamWrapper.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapper.java index f00b0aee..8e3a4744 100644 --- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapper.java +++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapper.java @@ -82,15 +82,15 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.List; -public class JsonUserListInputStreamWrapper implements InputStreamWrapper +public class JSONUserListInputStreamWrapper implements InputStreamWrapper { private static final Logger LOGGER = Logger - .getLogger(JsonUserListInputStreamWrapper.class); + .getLogger(JSONUserListInputStreamWrapper.class); private final List<User<HttpPrincipal>> output; - public JsonUserListInputStreamWrapper( - final List<User<HttpPrincipal>> output) + public JSONUserListInputStreamWrapper( + final List<User<HttpPrincipal>> output) { this.output = output; } 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 9bf89c7f..d3eade86 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 @@ -92,6 +92,7 @@ import ca.nrc.cadc.util.Log4jInit; import org.junit.Assert; import org.junit.Test; + import static org.easymock.EasyMock.*; @@ -105,6 +106,37 @@ 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() { @@ -151,52 +183,58 @@ public class GMSClientTest RegistryClient regClient = new RegistryClient(); URL baseURL = regClient.getServiceURL(new URI(AC.GMS_SERVICE_URI), - "https"); + "https"); final GMSClient client = new GMSClient(baseURL.toString()); Subject.doAs(subject, new PrivilegedExceptionAction<Object>() + { + @Override + public Object run() throws Exception { - @Override - public Object run() throws Exception - { - - List<Group> initial = client.getCachedGroups(test1UserID, Role.MEMBER, true); - Assert.assertNull("Cache should be null", initial); - // add single group as isMember might do - Group group0 = new Group("0"); - client.addCachedGroup(test1UserID, group0, Role.MEMBER); - List<Group> actual = client.getCachedGroups(test1UserID, Role.MEMBER, true); - Assert.assertNull("Cache should be null", actual); - - Group g = client.getCachedGroup(test1UserID, "0", Role.MEMBER); - Assert.assertNotNull("cached group from incomplete cache", g); - - // add all groups like getMemberships might do - List<Group> expected = new ArrayList<Group>(); - Group group1 = new Group("1"); - Group group2 = new Group("2"); - expected.add(group0); - expected.add(group1); - expected.add(group2); - - client.setCachedGroups(test1UserID, expected, Role.MEMBER); - - actual = client.getCachedGroups(test1UserID, Role.MEMBER, true); - Assert.assertEquals("Wrong cached groups", expected, actual); - - // check against another role - actual = client.getCachedGroups(test1UserID, Role.OWNER, true); - Assert.assertNull("Cache should be null", actual); - - // check against another userid - final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); - actual = client.getCachedGroups(anotherUserID, Role.MEMBER, true); - Assert.assertNull("Cache should be null", actual); - - return null; - } - }); + List<Group> initial = client + .getCachedGroups(test1UserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", initial); + + // add single group as isMember might do + Group group0 = new Group("0"); + client.addCachedGroup(test1UserID, group0, Role.MEMBER); + List<Group> actual = client + .getCachedGroups(test1UserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", actual); + + Group g = client + .getCachedGroup(test1UserID, "0", Role.MEMBER); + Assert.assertNotNull("cached group from incomplete cache", g); + + // add all groups like getMemberships might do + List<Group> expected = new ArrayList<Group>(); + Group group1 = new Group("1"); + Group group2 = new Group("2"); + expected.add(group0); + expected.add(group1); + expected.add(group2); + + client.setCachedGroups(test1UserID, expected, Role.MEMBER); + + actual = client + .getCachedGroups(test1UserID, Role.MEMBER, true); + Assert.assertEquals("Wrong cached groups", expected, actual); + + // check against another role + actual = client + .getCachedGroups(test1UserID, Role.OWNER, true); + Assert.assertNull("Cache should be null", actual); + + // check against another userid + final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); + actual = client + .getCachedGroups(anotherUserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", actual); + + return null; + } + }); subject = new Subject(); final HttpPrincipal test2UserID = new HttpPrincipal("test2"); @@ -204,41 +242,46 @@ public class GMSClientTest // do the same but as a different user Subject.doAs(subject, new PrivilegedExceptionAction<Object>() - { - @Override - public Object run() throws Exception - { + { + @Override + public Object run() throws Exception + { - List<Group> initial = client.getCachedGroups(test2UserID, Role.MEMBER, true); - Assert.assertNull("Cache should be null", initial); + List<Group> initial = client + .getCachedGroups(test2UserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", initial); - List<Group> expected = new ArrayList<Group>(); - Group group1 = new Group("1"); - Group group2 = new Group("2"); - expected.add(group1); - expected.add(group2); + List<Group> expected = new ArrayList<Group>(); + Group group1 = new Group("1"); + Group group2 = new Group("2"); + expected.add(group1); + expected.add(group2); - client.setCachedGroups(test2UserID, expected, Role.MEMBER); + client.setCachedGroups(test2UserID, expected, Role.MEMBER); - List<Group> actual = client.getCachedGroups(test2UserID, Role.MEMBER, true); - Assert.assertEquals("Wrong cached groups", expected, actual); + List<Group> actual = client + .getCachedGroups(test2UserID, Role.MEMBER, true); + Assert.assertEquals("Wrong cached groups", expected, actual); - // check against another role - actual = client.getCachedGroups(test2UserID, Role.OWNER, true); - Assert.assertNull("Cache should be null", actual); + // check against another role + actual = client + .getCachedGroups(test2UserID, Role.OWNER, true); + Assert.assertNull("Cache should be null", actual); - // check against another userid - final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); - actual = client.getCachedGroups(anotherUserID, Role.MEMBER, true); - Assert.assertNull("Cache should be null", actual); + // check against another userid + final HttpPrincipal anotherUserID = new HttpPrincipal("anotheruser"); + actual = client + .getCachedGroups(anotherUserID, Role.MEMBER, true); + Assert.assertNull("Cache should be null", actual); - return null; - } - }); + return null; + } + }); // do the same without a subject - List<Group> initial = client.getCachedGroups(test1UserID, Role.MEMBER, true); + List<Group> initial = client + .getCachedGroups(test1UserID, Role.MEMBER, true); Assert.assertNull("Cache should be null", initial); List<Group> newgroups = new ArrayList<Group>(); @@ -249,7 +292,8 @@ public class GMSClientTest client.setCachedGroups(test1UserID, newgroups, Role.MEMBER); - List<Group> actual = client.getCachedGroups(test1UserID, Role.MEMBER, true); + List<Group> actual = client + .getCachedGroups(test1UserID, Role.MEMBER, true); Assert.assertNull("Cache should still be null", actual); } catch (Throwable t) 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 index d8bd34ea..fe977e4e 100644 --- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapperTest.java +++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapperTest.java @@ -80,15 +80,15 @@ import org.junit.Test; import static org.junit.Assert.*; -public class JsonUserListInputStreamWrapperTest +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 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()); -- GitLab