Commit fed4862a authored by Brian Major's avatar Brian Major
Browse files

s1890 - Client changes so that URI is passed in constructor

parent 17870446
Loading
Loading
Loading
Loading
+8 −7
Original line number Diff line number Diff line
@@ -85,7 +85,8 @@ 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/canfargms";
    public static final String UMS_SERVICE_URI = "ivo://canfar.net/ums";
    public static final String GMS_SERVICE_URI = "ivo://canfar.net/gms";

    // Group URI attribute once the group name is appended
    public static final String GROUP_URI = "ivo://cadc.nrc.ca/gms#";
+51 −143
Original line number Diff line number Diff line
@@ -75,8 +75,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessControlContext;
import java.security.AccessControlException;
@@ -96,12 +96,12 @@ 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.User;
import ca.nrc.cadc.ac.UserNotFoundException;
import ca.nrc.cadc.ac.WriterException;
import ca.nrc.cadc.ac.xml.GroupListReader;
import ca.nrc.cadc.ac.xml.GroupReader;
import ca.nrc.cadc.ac.xml.GroupWriter;
import ca.nrc.cadc.auth.AuthMethod;
import ca.nrc.cadc.auth.AuthenticationUtil;
import ca.nrc.cadc.auth.HttpPrincipal;
import ca.nrc.cadc.auth.SSLUtil;
@@ -124,17 +124,22 @@ public class GMSClient implements TransferListener
{
    private static final Logger log = Logger.getLogger(GMSClient.class);

    private static final String GROUPS = "groups";
    private static final String SEARCH = "search";

    // socket factory to use when connecting
    private SSLSocketFactory sslSocketFactory;
    private SSLSocketFactory mySocketFactory;

    // client needs to know which service it is bound to and lookup
    // endpoints using RegistryClient
    private URI serviceURI;
    private RegistryClient registryClient;

    // storing baseURL is now considered bad form but fix is out of scope right now
    private String baseURL;
    private URI groupsURI;
    private URI searchURI;

    public GMSClient(URI serviceURI)
    {
        this(serviceURI, new RegistryClient());
    }

    /**
     * Slightly more complete constructor.  Tests can override the
@@ -145,57 +150,21 @@ public class GMSClient implements TransferListener
     */
    public GMSClient(URI serviceURI, RegistryClient registryClient)
    {
        try
        {
            URL base = registryClient.getServiceURL(serviceURI, "https");
            if (base == null)
                throw new IllegalArgumentException("service not found with https access: " + serviceURI);
            this.baseURL = base.toExternalForm();

            log.debug("AC Service URI: " + this.baseURL);
        }
        catch(MalformedURLException ex)
        {
            throw new RuntimeException("BUG: failed to construct GMS base URL", ex);
        }
    }
        if (serviceURI == null)
            throw new IllegalArgumentException("invalid serviceURI: " + serviceURI);
        if (serviceURI.getFragment() != null)
            throw new IllegalArgumentException("invalid serviceURI (fragment not allowed): " + serviceURI);

    public GMSClient(URI serviceURI)
    {
        this(serviceURI, new RegistryClient());
    }
        this.registryClient = registryClient;

    /**
     * Constructor.
     *
     * @param baseURL The URL of the supporting access control web service
     * obtained from the registry.
     * @deprecated
     */
    public GMSClient(String baseURL)
        throws IllegalArgumentException
    {
        if (baseURL == null)
        {
            throw new IllegalArgumentException("baseURL is required");
        }
        try
        {
            new URL(baseURL);
        }
        catch (MalformedURLException e)
        {
            throw new IllegalArgumentException("URL is malformed: " +
                                               e.getMessage());
        }

        if (baseURL.endsWith("/"))
        {
            this.baseURL = baseURL.substring(0, baseURL.length() - 1);
            this.groupsURI = new URI(serviceURI.toASCIIString() + "#" + GROUPS);
            this.searchURI = new URI(serviceURI.toASCIIString() + "#" + SEARCH);
        }
        else
        catch(URISyntaxException ex)
        {
            this.baseURL = baseURL;
            throw new RuntimeException("BUG: failed to create standardID from serviceURI + fragment", ex);
        }
    }

@@ -221,66 +190,6 @@ 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> getDisplayUsers() throws IOException
    {
        final List<User> webUsers = new ArrayList<User>();
        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> webUsers) throws IOException
    {
        final URL usersListURL = new URL(this.baseURL + "/users");
        return new HttpDownload(usersListURL,
                                new JsonUserListInputStreamWrapper(webUsers));
    }

    /**
     * Create a new group.
@@ -297,7 +206,7 @@ public class GMSClient implements TransferListener
        throws GroupAlreadyExistsException, AccessControlException,
               UserNotFoundException, WriterException, IOException
    {
        URL createGroupURL = new URL(this.baseURL + "/groups");
        URL createGroupURL = registryClient.getServiceURL(groupsURI, "https", "", AuthMethod.CERT);
        log.debug("createGroupURL request to " + createGroupURL.toString());

        // reset the state of the cache
@@ -368,7 +277,8 @@ public class GMSClient implements TransferListener
    public Group getGroup(String groupName)
        throws GroupNotFoundException, AccessControlException, IOException
    {
        URL getGroupURL = new URL(this.baseURL + "/groups/" + groupName);

        URL getGroupURL = registryClient.getServiceURL(groupsURI, "https", groupName, AuthMethod.CERT);
        log.debug("getGroup request to " + getGroupURL.toString());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        HttpDownload transfer = new HttpDownload(getGroupURL, out);
@@ -422,7 +332,8 @@ public class GMSClient implements TransferListener
    public List<String> getGroupNames()
        throws AccessControlException, IOException
    {
        final URL getGroupNamesURL = new URL(this.baseURL + "/groups");
        URL getGroupNamesURL = registryClient.getServiceURL(groupsURI, "https", "", AuthMethod.CERT);

        log.debug("getGroupNames request to " + getGroupNamesURL.toString());

        final List<String> groupNames = new ArrayList<String>();
@@ -498,7 +409,7 @@ public class GMSClient implements TransferListener
        throws IllegalArgumentException, GroupNotFoundException, UserNotFoundException,
               AccessControlException, WriterException, IOException
    {
        URL updateGroupURL = new URL(this.baseURL + "/groups/" + group.getID());
        URL updateGroupURL = registryClient.getServiceURL(groupsURI, "https", group.getID(), AuthMethod.CERT);
        log.debug("updateGroup request to " + updateGroupURL.toString());

        // reset the state of the cache
@@ -565,7 +476,7 @@ public class GMSClient implements TransferListener
    public void deleteGroup(String groupName)
        throws GroupNotFoundException, AccessControlException, IOException
    {
        URL deleteGroupURL = new URL(this.baseURL + "/groups/" + groupName);
        URL deleteGroupURL = registryClient.getServiceURL(groupsURI, "https", groupName, AuthMethod.CERT);
        log.debug("deleteGroup request to " + deleteGroupURL.toString());

        // reset the state of the cache
@@ -630,9 +541,9 @@ public class GMSClient implements TransferListener
        throws IllegalArgumentException, GroupNotFoundException,
               AccessControlException, IOException
    {
        URL addGroupMemberURL = new URL(this.baseURL + "/groups/" +
                                        targetGroupName + "/groupMembers/" +
                                        groupMemberName);

        String path = targetGroupName + "/groupMembers/" + groupMemberName;
        URL addGroupMemberURL = registryClient.getServiceURL(groupsURI, "https", path, AuthMethod.CERT);
        log.debug("addGroupMember request to " + addGroupMemberURL.toString());

        // reset the state of the cache
@@ -690,9 +601,8 @@ public class GMSClient implements TransferListener
        log.debug("addUserMember: " + targetGroupName + " + " + userID.getName());

        String userIDType = AuthenticationUtil.getPrincipalType(userID);
        URL addUserMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName
                + "/userMembers/" + NetUtil.encode(userID.getName())
                + "?idType=" + userIDType);
        String path = targetGroupName + "/userMembers/" + NetUtil.encode(userID.getName()) + "?idType=" + userIDType;
        URL addUserMemberURL = registryClient.getServiceURL(groupsURI, "https", path, AuthMethod.CERT);

        log.debug("addUserMember request to " + addUserMemberURL.toString());

@@ -745,9 +655,9 @@ public class GMSClient implements TransferListener
                                  String groupMemberName)
        throws GroupNotFoundException, AccessControlException, IOException
    {
        URL removeGroupMemberURL = new URL(this.baseURL + "/groups/" +
                                           targetGroupName + "/groupMembers/" +
                                           groupMemberName);

        String path = targetGroupName + "/groupMembers/" + groupMemberName;
        URL removeGroupMemberURL = registryClient.getServiceURL(groupsURI, "https", path, AuthMethod.CERT);
        log.debug("removeGroupMember request to " +
                  removeGroupMemberURL.toString());

@@ -813,10 +723,8 @@ public class GMSClient implements TransferListener
        String userIDType = AuthenticationUtil.getPrincipalType(userID);

        log.debug("removeUserMember: " + targetGroupName + " - " + userID.getName() + " type: " + userIDType);

        URL removeUserMemberURL = new URL(this.baseURL + "/groups/" + targetGroupName
                + "/userMembers/" + NetUtil.encode(userID.getName())
                + "?idType=" + userIDType);
        String path = targetGroupName + "/userMembers/" + NetUtil.encode(userID.getName()) + "?idType=" + userIDType;
        URL removeUserMemberURL = registryClient.getServiceURL(groupsURI, "https", path, AuthMethod.CERT);

        log.debug("removeUserMember: " + removeUserMemberURL.toString());

@@ -920,17 +828,17 @@ public class GMSClient implements TransferListener
        //String id = userID.getName();
        String roleString = role.getValue();

        StringBuilder searchGroupURL = new StringBuilder(this.baseURL);
        searchGroupURL.append("/search?");

        StringBuilder searchGroupPath = new StringBuilder("?");
        //searchGroupURL.append("ID=").append(NetUtil.encode(id));
        //searchGroupURL.append("&IDTYPE=").append(NetUtil.encode(idType));
        searchGroupURL.append("&ROLE=").append(NetUtil.encode(roleString));
        searchGroupPath.append("&ROLE=").append(NetUtil.encode(roleString));

        log.debug("getMemberships request to " + searchGroupURL.toString());
        URL searchURL = registryClient.getServiceURL(searchURI, "https", searchGroupPath.toString(), AuthMethod.CERT);

        log.debug("getMemberships request to " + searchURL.toString());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        URL url = new URL(searchGroupURL.toString());
        HttpDownload transfer = new HttpDownload(url, out);
        HttpDownload transfer = new HttpDownload(searchURL, out);

        transfer.setSSLSocketFactory(getSSLSocketFactory());
        transfer.run();
@@ -1028,18 +936,18 @@ public class GMSClient implements TransferListener
        //String id = userID.getName();
        String roleString = role.getValue();

        StringBuilder searchGroupURL = new StringBuilder(this.baseURL);
        searchGroupURL.append("/search?");
        StringBuilder searchGroupPath = new StringBuilder("?");

        //searchGroupURL.append("ID=").append(NetUtil.encode(id));
        //searchGroupURL.append("&IDTYPE=").append(NetUtil.encode(idType));
        searchGroupURL.append("&ROLE=").append(NetUtil.encode(roleString));
        searchGroupURL.append("&GROUPID=").append(NetUtil.encode(groupName));
        searchGroupPath.append("&ROLE=").append(NetUtil.encode(roleString));
        searchGroupPath.append("&GROUPID=").append(NetUtil.encode(groupName));

        URL searchURL = registryClient.getServiceURL(searchURI, "https", searchGroupPath.toString(), AuthMethod.CERT);

        log.debug("getMembership request to " + searchGroupURL.toString());
        log.debug("getMembership request to " + searchURL.toString());
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        URL url = new URL(searchGroupURL.toString());
        HttpDownload transfer = new HttpDownload(url, out);
        HttpDownload transfer = new HttpDownload(searchURL, out);

        transfer.setSSLSocketFactory(getSSLSocketFactory());
        transfer.run();
+88 −42
Original line number Diff line number Diff line
@@ -69,9 +69,15 @@
package ca.nrc.cadc.ac.client;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessControlException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import javax.security.auth.Subject;
@@ -81,10 +87,12 @@ import org.apache.log4j.Logger;

import ca.nrc.cadc.ac.User;
import ca.nrc.cadc.ac.xml.UserReader;
import ca.nrc.cadc.auth.AuthMethod;
import ca.nrc.cadc.auth.AuthenticationUtil;
import ca.nrc.cadc.auth.NumericPrincipal;
import ca.nrc.cadc.net.HttpDownload;
import ca.nrc.cadc.net.NetUtil;
import ca.nrc.cadc.reg.client.RegistryClient;


/**
@@ -95,8 +103,16 @@ public class UserClient
{
    private static final Logger log = Logger.getLogger(UserClient.class);

    // socket factory to use when connecting
    private String baseURL;
    private static final String USERS = "users";
    private static final String USER_REQUESTS = "reqs";

    private RegistryClient registryClient;

    private URI usersURI;

    // to be used when the client can work with
    // user requests
    private URI userReqsURI;

    /**
     * Constructor.
@@ -104,30 +120,29 @@ public class UserClient
     * @param baseURL The URL of the supporting access control web service
     *                obtained from the registry.
     */
    public UserClient(final String baseURL)
    public UserClient(URI serviceURI)
            throws IllegalArgumentException
    {
        if (baseURL == null)
        {
            throw new IllegalArgumentException("baseURL is required");
        this(serviceURI, new RegistryClient());
    }
        try
        {
            new URL(baseURL);
        }
        catch (MalformedURLException e)

    public UserClient(URI serviceURI, RegistryClient registryClient)
    {
            throw new IllegalArgumentException("URL is malformed: " +
                                               e.getMessage());
        }
        if (serviceURI == null)
            throw new IllegalArgumentException("invalid serviceURI: " + serviceURI);
        if (serviceURI.getFragment() != null)
            throw new IllegalArgumentException("invalid serviceURI (fragment not allowed): " + serviceURI);

        if (baseURL.endsWith("/"))
        this.registryClient = registryClient;

        try
        {
            this.baseURL = baseURL.substring(0, baseURL.length() - 1);
            this.usersURI = new URI(serviceURI.toASCIIString() + "#" + USERS);
            this.userReqsURI = new URI(serviceURI.toASCIIString() + "#" + USER_REQUESTS);
        }
        else
        catch(URISyntaxException ex)
        {
            this.baseURL = baseURL;
            throw new RuntimeException("BUG: failed to create standardID from serviceURI + fragment", ex);
        }
    }

@@ -137,16 +152,23 @@ public class UserClient
     * associated principals which are then added to the subject.
     *
     * @param subject           The Subject to pull Princials for.
     * @throws MalformedURLException
     */
    public void augmentSubject(Subject subject)
    public void augmentSubject(Subject subject) throws MalformedURLException
    {
    	Principal principal = this.getPrincipal(subject);
    	if (principal != null)
    	{
	        URL url = this.getURL(principal);
	    	log.debug("augmentSubject request to " + url.toString());

	        String userID = principal.getName();
	        String path = NetUtil.encode(userID) + "?idType=" + this.getIdType(principal) + "&detail=identity";

	        // augment subject calls are always https with client certs
	        URL getUserURL = registryClient.getServiceURL(usersURI, "https", path, AuthMethod.CERT);

	    	log.debug("augmentSubject request to " + getUserURL.toString());
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        HttpDownload download = new HttpDownload(url, out);
	        HttpDownload download = new HttpDownload(getUserURL, out);
	        download.run();

	        int responseCode = download.getResponseCode();
@@ -169,7 +191,49 @@ public class UserClient
    	}
    }

    /**
     * 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> getDisplayUsers() throws IOException
    {
        URL usersURL = registryClient.getServiceURL(usersURI, "https");
        final List<User> webUsers = new ArrayList<User>();
        HttpDownload httpDownload = new HttpDownload(usersURL, new JsonUserListInputStreamWrapper(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;
    }

    protected Principal getPrincipal(final Subject subject)
    {
@@ -217,24 +281,6 @@ public class UserClient
    	}
    }

    protected URL getURL(Principal principal)
    {
		try
		{
		    String userID = principal.getName();
			URL url = new URL(this.baseURL + "/users/" + NetUtil.encode(userID) +
					"?idType=" + this.getIdType(principal) + "&detail=identity");
			log.debug("getURL(): returned url ="
					+ ""
					+ " " + url.toString());
			return url;
		}
		catch (MalformedURLException e)
		{
			throw new RuntimeException(e);
		}
    }

    protected String getIdType(Principal principal)
    {
		String idTypeStr = AuthenticationUtil.getPrincipalType(principal);
+8 −48
Original line number Diff line number Diff line
@@ -69,19 +69,21 @@

package ca.nrc.cadc.ac.client;

import java.io.IOException;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;

import java.net.URI;
import java.net.URL;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
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.junit.Assert;
import org.junit.Test;

import ca.nrc.cadc.ac.Group;
import ca.nrc.cadc.ac.Role;
@@ -89,11 +91,6 @@ 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
{
@@ -102,43 +99,6 @@ public class GMSClientTest
        Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO);
    }

    @Test
    public void testGetDisplayUsers() throws Exception
    {
        final HttpDownload mockHTTPDownload = createMock(HttpDownload.class);
        final RegistryClient mockRegistryClient =
                createMock(RegistryClient.class);
        final URI serviceURI = URI.create("http://mysite.com/users");

        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();

        expect(mockRegistryClient.getServiceURL(serviceURI, "https")).andReturn(
                new URL("http://mysite.com/users/endpoint"));

        replay(mockHTTPDownload, mockRegistryClient);
        final GMSClient testSubject =
                new GMSClient(serviceURI, mockRegistryClient)
                {
                    @Override
                    HttpDownload createDisplayUsersHTTPDownload(
                            List<User> webUsers) throws IOException
                    {
                        return mockHTTPDownload;
                    }
                };

        testSubject.getDisplayUsers();
        verify(mockHTTPDownload, mockRegistryClient);
    }


    @Test
@@ -172,7 +132,6 @@ public class GMSClientTest
        Assert.assertTrue(client.userIsSubject(userID, subject));
        Assert.assertFalse(client.userIsSubject(userID2, subject));
        Assert.assertTrue(client.userIsSubject(userID3, subject));
        verify(mockRegistryClient);
    }

    @Test
@@ -191,7 +150,7 @@ public class GMSClientTest

        replay(mockRegistryClient);
        final GMSClient client = new GMSClient(serviceURI, mockRegistryClient);
        verify(mockRegistryClient);


        Subject.doAs(subject, new PrivilegedExceptionAction<Object>()
        {
@@ -243,6 +202,7 @@ public class GMSClientTest
            }
        });


        subject = new Subject();
        final HttpPrincipal test2UserID = new HttpPrincipal("test2");
        subject.getPrincipals().add(test2UserID);
+7 −34

File changed.

Preview size limit exceeded, changes collapsed.