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 30507fe9b9ee4449d9d0b40bc1c1b93d0dbfab4c..666d6bfbd575578d72db554c715e2bd1a5490417 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
@@ -96,6 +96,7 @@ import ca.nrc.cadc.ac.GroupAlreadyExistsException;
 import ca.nrc.cadc.ac.GroupNotFoundException;
 import ca.nrc.cadc.ac.GroupReader;
 import ca.nrc.cadc.ac.GroupWriter;
+import ca.nrc.cadc.ac.GroupsReader;
 import ca.nrc.cadc.ac.Role;
 import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.auth.AuthenticationUtil;
@@ -653,27 +654,147 @@ public class GMSClient
         }
     }
 
-    public Collection<Group> getMemberships(Principal userID, Role role)
+    public List<Group> getMemberships(Principal userID, Role role)
+        throws IOException
     {
-        throw new UnsupportedOperationException();
+        if (userID == null || role == null)
+        {
+            throw new IllegalArgumentException("userID and role are required.");
+        }
+        
+        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=" + URLEncoder.encode(id, "UTF-8"));
+        searchGroupURL.append("&TYPE=" + URLEncoder.encode(idType, "UTF-8"));
+        searchGroupURL.append("&ROLE=" + URLEncoder.encode(roleString, "UTF-8"));
+        
+        log.debug("getMemberships request to " + searchGroupURL.toString());
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        URL url = new URL(searchGroupURL.toString());
+        HttpDownload transfer = new HttpDownload(url, out);
+
+        transfer.setSSLSocketFactory(getSSLSocketFactory());
+        transfer.run();
+
+        Throwable error = transfer.getThrowable();
+        if (error != null)
+        {
+            log.debug("getMemberships throwable", error);
+            // transfer returns a -1 code for anonymous access.
+            if ((transfer.getResponseCode() == -1) || 
+                (transfer.getResponseCode() == 401) || 
+                (transfer.getResponseCode() == 403))
+            {
+                throw new AccessControlException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 400)
+            {
+                throw new IllegalArgumentException(error.getMessage());
+            }
+            throw new IOException(error);
+        }
+
+        try
+        {
+            String groupsXML = new String(out.toByteArray(), "UTF-8");
+            log.debug("getMemberships returned: " + groupsXML);
+            return GroupsReader.read(groupsXML);
+        }
+        catch (Exception bug)
+        {
+            log.error("Unexpected exception", bug);
+            throw new RuntimeException(bug);
+        }
     }
     
     public Group getMembership(Principal userID, String groupName)
+        throws IOException
     {
         return getMembership(userID, groupName, Role.MEMBER);
     }
     
     public Group getMembership(Principal userID, String groupName, Role role)
+        throws IOException
     {
-        throw new UnsupportedOperationException();
+        if (userID == null || groupName == null || role == null)
+        {
+            throw new IllegalArgumentException("userID and role are required.");
+        }
+        
+        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=" + URLEncoder.encode(id, "UTF-8"));
+        searchGroupURL.append("&TYPE=" + URLEncoder.encode(idType, "UTF-8"));
+        searchGroupURL.append("&ROLE=" + URLEncoder.encode(roleString, "UTF-8"));
+        searchGroupURL.append("&GURI=" + URLEncoder.encode(groupName, "UTF-8"));
+        
+        log.debug("getMembership request to " + searchGroupURL.toString());
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        URL url = new URL(searchGroupURL.toString());
+        HttpDownload transfer = new HttpDownload(url, out);
+
+        transfer.setSSLSocketFactory(getSSLSocketFactory());
+        transfer.run();
+
+        Throwable error = transfer.getThrowable();
+        if (error != null)
+        {
+            log.debug("getMembership throwable", error);
+            // transfer returns a -1 code for anonymous access.
+            if ((transfer.getResponseCode() == -1) || 
+                (transfer.getResponseCode() == 401) || 
+                (transfer.getResponseCode() == 403))
+            {
+                throw new AccessControlException(error.getMessage());
+            }
+            if (transfer.getResponseCode() == 400)
+            {
+                throw new IllegalArgumentException(error.getMessage());
+            }
+            throw new IOException(error);
+        }
+
+        try
+        {
+            String groupsXML = new String(out.toByteArray(), "UTF-8");
+            log.debug("getMembership returned: " + groupsXML);
+            List<Group> groups = GroupsReader.read(groupsXML);
+            if (groups.size() == 0)
+            {
+                return null;
+            }
+            if (groups.size() == 1)
+            {
+                return groups.get(0);
+            }
+            throw new IllegalStateException(
+                    "Duplicate membership for " + id + " in group " + groupName);
+        }
+        catch (Exception bug)
+        {
+            log.error("Unexpected exception", bug);
+            throw new RuntimeException(bug);
+        }
     }
     
     public boolean isMember(Principal userID, String groupName)
+        throws IOException
     {
         return isMember(userID, groupName, Role.MEMBER);
     }
     
     public boolean isMember(Principal userID, String groupName, Role role)
+        throws IOException
     {
         Group group = getMembership(userID, groupName, role);
         return group != null;