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 261a64ee49997d1d9e68f4ec6ef82a5aaa70a144..666d6bfbd575578d72db554c715e2bd1a5490417 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
@@ -68,18 +68,6 @@
  */
 package ca.nrc.cadc.ac.client;
 
-import ca.nrc.cadc.ac.Group;
-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.UserNotFoundException;
-import ca.nrc.cadc.auth.AuthenticationUtil;
-import ca.nrc.cadc.auth.SSLUtil;
-import ca.nrc.cadc.net.HttpDownload;
-import ca.nrc.cadc.net.HttpPost;
-import ca.nrc.cadc.net.HttpUpload;
-import ca.nrc.cadc.net.NetUtil;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -96,13 +84,32 @@ import java.util.Collection;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLSocketFactory;
 import javax.security.auth.Subject;
+
 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.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;
+import ca.nrc.cadc.auth.SSLUtil;
+import ca.nrc.cadc.net.HttpDownload;
+import ca.nrc.cadc.net.HttpPost;
+import ca.nrc.cadc.net.HttpUpload;
+import ca.nrc.cadc.net.NetUtil;
+
 /**
  * Client class for communicating with the access control web service.
+ * 
+ * TODO: Cache the group memberships using getCachedGroups(), setCachedGroups()
  */
 public class GMSClient
 {
@@ -647,9 +654,150 @@ public class GMSClient
         }
     }
 
-    public Collection<Group> searchGroups()
+    public List<Group> getMemberships(Principal userID, Role role)
+        throws IOException
+    {
+        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
+    {
+        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
     {
-        throw new UnsupportedOperationException();
+        Group group = getMembership(userID, groupName, role);
+        return group != null;
     }
 
     /**