Newer
Older
* identified by userID, is a member (of type role) of that group.
* Return null otherwise.
* @param userID Identifies the user.
* @param groupName Identifies the group.
* @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 IllegalArgumentException If a parameter is null.
* @throws IOException If an unknown error occured.
*/
public Group getMembership(Principal userID, String groupName, Role role)
throws UserNotFoundException, AccessControlException, IOException
if (userID == null || groupName == null || role == null)
{
throw new IllegalArgumentException("userID and role are required.");
}
Patrick Dowler
committed
Group cachedGroup = getCachedGroup(userID, groupName, role);
if (cachedGroup != null)
Patrick Dowler
committed
return cachedGroup;
String idType = AuthenticationUtil.getPrincipalType(userID);
String id = userID.getName();
String roleString = role.getValue();
StringBuilder searchGroupURL = new StringBuilder(this.baseURL);
searchGroupURL.append("/search?");
Patrick Dowler
committed
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));
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() == 404)
{
throw new UserNotFoundException(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);
GroupListReader groupListReader = new GroupListReader();
List<Group> groups = groupListReader.read(groupsXML);
if (groups.size() == 0)
{
return null;
}
if (groups.size() == 1)
{
Patrick Dowler
committed
Group ret = groups.get(0);
addCachedGroup(userID, ret, role);
return ret;
}
throw new IllegalStateException(
"Duplicate membership for " + id + " in group " + groupName);
}
catch (Exception bug)
{
log.error("Unexpected exception", bug);
throw new RuntimeException(bug);
}
/**
* Check group membership of the current Subject.
* @param groupName
* @return
* @throws UserNotFoundException
* @throws AccessControlException
*/
public boolean isMember(String groupName)
throws UserNotFoundException, AccessControlException, IOException
{
return isMember(getCurrentUserID(), groupName, Role.MEMBER);
}
/**
* Check if userID is a member of groupName.
* This is equivalent to isMember(userID, groupName, Role.MEMBER)
* @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 IllegalArgumentException If a parameter is null.
* @throws IOException If an unknown error occured.
*/
public boolean isMember(Principal userID, String groupName)
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 groupName Identifies the group.
* @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 IllegalArgumentException If a parameter is null.
* @throws IOException If an unknown error occured.
*/
public boolean isMember(Principal userID, String groupName, Role role)
throws UserNotFoundException, AccessControlException, IOException
{
Group group = getMembership(userID, groupName, role);
return group != null;
}
/**
* @param sslSocketFactory the sslSocketFactory to set
*/
public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory)
{
Patrick Dowler
committed
if (mySocketFactory != null)
throw new IllegalStateException("Illegal use of GMSClient: "
+ "cannot set SSLSocketFactory after using one created from Subject");
Patrick Dowler
committed
private int subjectHashCode = 0;
private SSLSocketFactory getSSLSocketFactory()
{
Patrick Dowler
committed
AccessControlContext ac = AccessController.getContext();
Subject s = Subject.getSubject(ac);
Patrick Dowler
committed
// no real Subject: can only use the one from setSSLSocketFactory
if (s == null || s.getPrincipals().isEmpty())
{
return sslSocketFactory;
}
Patrick Dowler
committed
// lazy init
if (this.mySocketFactory == null)
{
log.debug("getSSLSocketFactory: " + s);
this.mySocketFactory = SSLUtil.getSocketFactory(s);
this.subjectHashCode = s.hashCode();
}
else
Patrick Dowler
committed
int c = s.hashCode();
if (c != subjectHashCode)
throw new IllegalStateException("Illegal use of "
Patrick Dowler
committed
+ this.getClass().getSimpleName()
+ ": subject change not supported for internal SSLSocketFactory");
Patrick Dowler
committed
return this.mySocketFactory;
protected void clearCache()
{
AccessControlContext acContext = AccessController.getContext();
Subject subject = Subject.getSubject(acContext);
if (subject != null)
{
Patrick Dowler
committed
subject.getPrivateCredentials().remove(new GroupMemberships());
Patrick Dowler
committed
protected GroupMemberships getGroupCache(Principal userID)
{
AccessControlContext acContext = AccessController.getContext();
Subject subject = Subject.getSubject(acContext);
// only consult cache if the userID is of the calling subject
if (userIsSubject(userID, subject))
Patrick Dowler
committed
Set<GroupMemberships> gset = subject.getPrivateCredentials(GroupMemberships.class);
if (gset == null || gset.isEmpty())
Patrick Dowler
committed
GroupMemberships mems = new GroupMemberships();
subject.getPrivateCredentials().add(mems);
return mems;
Patrick Dowler
committed
GroupMemberships mems = gset.iterator().next();
return mems;
Patrick Dowler
committed
return null; // no cache
}
Patrick Dowler
committed
protected Group getCachedGroup(Principal userID, String groupID, Role role)
{
List<Group> groups = getCachedGroups(userID, role, false);
if (groups == null)
return null; // no cache
for (Group g : groups)
{
if (g.getID().equals(groupID))
return g;
Patrick Dowler
committed
protected List<Group> getCachedGroups(Principal userID, Role role, boolean complete)
{
GroupMemberships mems = getGroupCache(userID);
if (mems == null)
return null; // no cache
Patrick Dowler
committed
Boolean cacheState = mems.complete.get(role);
if (!complete || Boolean.TRUE.equals(cacheState))
return mems.memberships.get(role);
Patrick Dowler
committed
// caller wanted complete and we don't have that
Patrick Dowler
committed
protected void addCachedGroup(Principal userID, Group group, Role role)
Patrick Dowler
committed
GroupMemberships mems = getGroupCache(userID);
if (mems == null)
return; // no cache
Patrick Dowler
committed
List<Group> groups = mems.memberships.get(role);
if (groups == null)
Patrick Dowler
committed
groups = new ArrayList<Group>();
mems.complete.put(role, Boolean.FALSE);
mems.memberships.put(role, groups);
}
if (!groups.contains(group))
groups.add(group);
}
protected void setCachedGroups(Principal userID, List<Group> groups, Role role)
Patrick Dowler
committed
GroupMemberships mems = getGroupCache(userID);
if (mems == null)
return; // no cache
Patrick Dowler
committed
log.debug("Caching groups for " + userID + ", role " + role);
List<Group> cur = mems.memberships.get(role);
if (cur == null)
Patrick Dowler
committed
cur = new ArrayList<Group>();
mems.complete.put(role, Boolean.FALSE);
mems.memberships.put(role, cur);
}
for (Group group : groups)
{
if (!cur.contains(group))
cur.add(group);
mems.complete.put(role, Boolean.TRUE);
protected boolean userIsSubject(Principal userID, Subject subject)
{
if (userID == null || subject == null)
{
return false;
}
for (Principal subjectPrincipal : subject.getPrincipals())
Patrick Dowler
committed
if (AuthenticationUtil.equals(subjectPrincipal, userID))
Patrick Dowler
committed
* Class used to hold list of groups in which a user is known to be a member.
Patrick Dowler
committed
protected class GroupMemberships implements Comparable
Map<Role, List<Group>> memberships = new HashMap<Role, List<Group>>();
Patrick Dowler
committed
Map<Role, Boolean> complete = new HashMap<Role, Boolean>();
Patrick Dowler
committed
// only allow one in a set - makes clearCache simple too
public boolean equals(Object rhs)
{
if (rhs != null && rhs instanceof GroupMemberships)
return true;
return false;
}
public int compareTo(Object t)
{
if (this.equals(t))
return 0;
return -1; // wonder if this is sketchy
}