Skip to content
Snippets Groups Projects
Commit 1ca469a9 authored by Dustin Jenkins's avatar Dustin Jenkins
Browse files

Merge branch 's1832' of ssh://gimli2.cadc.dao.nrc.ca/srv/cadc/git/wopencadc into s1832

parents 773fa9a3 e00017cd
No related branches found
No related tags found
No related merge requests found
......@@ -173,21 +173,5 @@ public interface GroupPersistence<T extends Principal>
Collection<Group> getGroups(T userID, Role role, String groupID)
throws UserNotFoundException, GroupNotFoundException,
TransientException, AccessControlException;
/**
* Check whether the user is a member of the group.
*
* @param userID The userID.
* @param groupID The groupID.
*
* @return true or false
*
* @throws UserNotFoundException If the user is not found.
* @throws TransientException If an temporary, unexpected problem occurred.
* @throws AccessControlException If the operation is not permitted.
*/
boolean isMember(T userID, String groupID)
throws UserNotFoundException, TransientException,
AccessControlException;
}
......@@ -126,6 +126,9 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
};
private LdapUserDAO<T> userPersist;
// this gets filled by the LdapgroupPersistence
GroupDetailSelector searchDetailSelector;
public LdapGroupDAO(LdapConfig config, LdapUserDAO<T> userPersist)
{
......@@ -811,8 +814,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
}
throw new RuntimeException("BUG: failed to extract group name from " + groupDN.toString());
}
// this gets filled by the LdapgroupPersistence
GroupDetailSelector searchDetailSelector;
private boolean isDetailedSearch(Group g, Role r)
{
......
......@@ -97,7 +97,7 @@ public class LdapGroupPersistence<T extends Principal>
config = LdapConfig.getLdapConfig();
}
protected void setDetailSelector(GroupDetailSelector gds)
public void setDetailSelector(GroupDetailSelector gds)
{
this.detailSelector = gds;
}
......@@ -256,14 +256,5 @@ public class LdapGroupPersistence<T extends Principal>
userDAO.close();
}
}
}
public boolean isMember(T userID, String groupID)
throws UserNotFoundException, TransientException,
AccessControlException
{
return (new LdapUserPersistence<T>()).isMember(userID, groupID);
}
}
}
......@@ -70,17 +70,30 @@ package ca.nrc.cadc.ac.server.web;
import java.io.IOException;
import java.security.AccessControlException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import javax.security.auth.Subject;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import ca.nrc.cadc.ac.Group;
import ca.nrc.cadc.ac.Role;
import ca.nrc.cadc.ac.UserNotFoundException;
import ca.nrc.cadc.ac.server.GroupDetailSelector;
import ca.nrc.cadc.ac.server.UserPersistence;
import ca.nrc.cadc.ac.server.ldap.LdapGroupPersistence;
import ca.nrc.cadc.ac.server.ldap.LdapUserPersistence;
import ca.nrc.cadc.auth.AuthenticatorImpl;
import ca.nrc.cadc.auth.HttpPrincipal;
import ca.nrc.cadc.auth.SSOCookieManager;
import ca.nrc.cadc.log.ServletLogInfo;
import ca.nrc.cadc.net.TransientException;
import ca.nrc.cadc.util.StringUtil;
@SuppressWarnings("serial")
......@@ -88,6 +101,32 @@ public class LoginServlet extends HttpServlet
{
private static final Logger log = Logger.getLogger(LoginServlet.class);
private static final String CONTENT_TYPE = "text/plain";
public static final String PROXY_USER_DELIM = " as ";
String proxyGroup; // only users in this group can impersonate other users
String nonImpersonGroup; // users in this group cannot be impersonated
private static final String PROXY_ACCESS = "Proxy user access: ";
@Override
public void init(final ServletConfig config) throws ServletException
{
super.init(config);
try
{
this.proxyGroup = config.getInitParameter(
LoginServlet.class.getName() + ".proxyGroup");
log.info("proxyGroup: " + proxyGroup);
this.nonImpersonGroup = config.getInitParameter(
LoginServlet.class.getName() + ".nonImpersonGroup");
log.info("nonImpersonGroup: " + nonImpersonGroup);
}
catch(Exception ex)
{
log.error("failed to init: " + ex);
}
}
/**
* Attempt to login for userid/password.
*/
......@@ -101,14 +140,28 @@ public class LoginServlet extends HttpServlet
{
log.info(logInfo.start());
String userID = request.getParameter("username");
String proxyUser = null;
if (userID.contains(PROXY_USER_DELIM))
{
String[] fields = userID.split(PROXY_USER_DELIM);
proxyUser = fields[0];
userID = fields[1];
checkCanImpersonate(userID, proxyUser);
}
String password = request.getParameter("password");
UserPersistence up = new LdapUserPersistence();
if (StringUtil.hasText(userID))
{
if (StringUtil.hasText(password))
{
if (new LdapUserPersistence().doLogin(userID, password))
{
String token = new SSOCookieManager().generate(new HttpPrincipal(userID));
if ((StringUtil.hasText(proxyUser) &&
up.doLogin(proxyUser, password)) ||
(!StringUtil.hasText(proxyUser) &&
up.doLogin(userID, password)))
{
String token =
new SSOCookieManager().generate(
new HttpPrincipal(userID, proxyUser));
response.setContentType(CONTENT_TYPE);
response.setContentLength(token.length());
response.getWriter().write(token);
......@@ -126,16 +179,24 @@ public class LoginServlet extends HttpServlet
}
catch (IllegalArgumentException e)
{
log.debug(e.getMessage(), e);
logInfo.setMessage(e.getMessage());
String msg = e.getMessage();
if (msg.startsWith(PROXY_ACCESS))
{
log.warn(msg, e);
}
else
{
log.debug(msg, e);
}
logInfo.setMessage(msg);
response.setContentType(CONTENT_TYPE);
response.getWriter().write(e.getMessage());
response.getWriter().write(msg);
response.setStatus(400);
}
catch (AccessControlException e)
{
log.debug(e.getMessage(), e);
String message = "Invalid credentials";
String message = e.getMessage();
logInfo.setMessage(message);
response.setContentType(CONTENT_TYPE);
response.getWriter().write(message);
......@@ -157,4 +218,89 @@ public class LoginServlet extends HttpServlet
log.info(logInfo.end());
}
}
/**
* Checks if user can impersonate another user
*/
protected void checkCanImpersonate(final String userID, final String proxyUser)
throws AccessControlException, UserNotFoundException,
TransientException, Throwable
{
final LdapGroupPersistence<HttpPrincipal> gp =
getLdapGroupPersistence();
AuthenticatorImpl ai = new AuthenticatorImpl();
Subject proxySubject = new Subject();
proxySubject.getPrincipals().add(new HttpPrincipal(proxyUser));
ai.augmentSubject(proxySubject);
try
{
Subject.doAs(proxySubject, new PrivilegedExceptionAction<Object>()
{
@Override
public Object run() throws Exception
{
if (gp.getGroups(new HttpPrincipal(proxyUser), Role.MEMBER,
proxyGroup).size() == 0)
{
throw new AccessControlException(PROXY_ACCESS
+ proxyUser + " as " + userID
+ " failed - not allowed to impersonate ("
+ proxyUser + " not in " + proxyGroup
+ " group)");
}
return null;
}
});
Subject userSubject = new Subject();
userSubject.getPrincipals().add(new HttpPrincipal(userID));
ai.augmentSubject(userSubject);
Subject.doAs(userSubject, new PrivilegedExceptionAction<Object>()
{
@Override
public Object run() throws Exception
{
if (gp.getGroups(new HttpPrincipal(userID), Role.MEMBER,
nonImpersonGroup).size() != 0)
{
throw new AccessControlException(PROXY_ACCESS
+ proxyUser + " as " + userID
+ " failed - non impersonable (" + userID
+ " in " + nonImpersonGroup + " group)");
}
return null;
}
});
}
catch (PrivilegedActionException e)
{
Throwable cause = e.getCause();
if (cause != null)
{
throw cause;
}
Exception exception = e.getException();
if (exception != null)
{
throw exception;
}
throw e;
}
}
protected LdapGroupPersistence<HttpPrincipal> getLdapGroupPersistence()
{
LdapGroupPersistence<HttpPrincipal> gp = new LdapGroupPersistence<HttpPrincipal>();
gp.setDetailSelector(new GroupDetailSelector()
{
@Override
public boolean isDetailedSearch(Group g, Role r)
{
return false;
}
});
return gp;
}
}
......@@ -124,7 +124,7 @@ public class AuthenticatorImpl implements Authenticator
return subject;
}
protected void augmentSubject(final Subject subject)
public void augmentSubject(final Subject subject)
{
try
......
package ca.nrc.cadc.ac.server.web.users;
import java.security.AccessControlException;
import java.util.Collection;
import java.util.HashSet;
import org.easymock.EasyMock;
import org.junit.Test;
import ca.nrc.cadc.ac.Group;
import ca.nrc.cadc.ac.Role;
import ca.nrc.cadc.ac.server.GroupDetailSelector;
import ca.nrc.cadc.ac.server.ldap.LdapGroupPersistence;
import ca.nrc.cadc.auth.HttpPrincipal;
import static org.junit.Assert.fail;
import static org.junit.Assert.assertTrue;
public class UserLoginServletTest
{
@Test
public void getCheckCanImpersonate() throws Throwable
{
LoginServlet ls = new LoginServlet()
{
/**
*
*/
private static final long serialVersionUID = 1L;
@Override
protected LdapGroupPersistence<HttpPrincipal> getLdapGroupPersistence()
{
proxyGroup = "proxyGroup";
nonImpersonGroup = "niGroup";
Collection<Group> proxyGroups = new HashSet<Group>();
proxyGroups.add(new Group(proxyGroup));
Collection<Group> niGroups = new HashSet<Group>();
niGroups.add(new Group(nonImpersonGroup));
// mock returns a shell instance
@SuppressWarnings("unchecked")
LdapGroupPersistence<HttpPrincipal> mockGp =
(LdapGroupPersistence<HttpPrincipal>)EasyMock
.createMock(LdapGroupPersistence.class);
mockGp.setDetailSelector(new GroupDetailSelector()
{
@Override
public boolean isDetailedSearch(Group g, Role r)
{
return false;
}
});
try
{
EasyMock.expect(
mockGp.getGroups(new HttpPrincipal("proxyUser"),
Role.MEMBER, proxyGroup)).andReturn(
proxyGroups);
EasyMock.expect(
mockGp.getGroups(new HttpPrincipal("nonProxyUser"),
Role.MEMBER, proxyGroup)).andReturn(
new HashSet<Group>());
EasyMock.expect(
mockGp.getGroups(new HttpPrincipal("user"),
Role.MEMBER, nonImpersonGroup)).andReturn(
new HashSet<Group>());
EasyMock.expect(
mockGp.getGroups(new HttpPrincipal("niUser"),
Role.MEMBER, nonImpersonGroup)).andReturn(
niGroups);
EasyMock.replay(mockGp);
} catch (Exception e)
{
throw new RuntimeException(e);
}
return mockGp;
}
};
// proxyUser can impersonate user
ls.checkCanImpersonate("user", "proxyUser");
// nonProxyUser cannot impersonate
try
{
ls.checkCanImpersonate("user", "nonProxyUser");
fail("AccessControlException expected");
} catch (AccessControlException ex)
{
assertTrue(ex.getMessage().contains("not allowed to impersonate"));
}
// niUser cannot be impersonated
try
{
ls.checkCanImpersonate("niUser", "proxyUser");
fail("AccessControlException expected");
} catch (AccessControlException ex)
{
assertTrue(ex.getMessage().contains("non impersonable"));
}
// nonProxyUser cannot impersonate and niUser cannot be impersonated
try
{
ls.checkCanImpersonate("niUser", "nonProxyUser");
fail("AccessControlException expected");
} catch (AccessControlException ex)
{
assertTrue(ex.getMessage().contains("not allowed to impersonate"));
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment