Commit 6e249e7c authored by Adrian Damian's avatar Adrian Damian
Browse files

Added support for proxy User (superuser) authentication

parent c5c8fdb2
Loading
Loading
Loading
Loading
+0 −16
Original line number Diff line number Diff line
@@ -174,20 +174,4 @@ public interface GroupPersistence<T extends Principal>
        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;
  
}
+4 −2
Original line number Diff line number Diff line
@@ -127,6 +127,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)
    {
        super(config);
@@ -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)
    {
+2 −11
Original line number Diff line number Diff line
@@ -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;
    }
@@ -257,13 +257,4 @@ public class LdapGroupPersistence<T extends Principal>
            }
        }
    }  
    
    public boolean isMember(T userID, String groupID)
            throws UserNotFoundException, TransientException,
            AccessControlException
    {
        return (new LdapUserPersistence<T>()).isMember(userID, groupID);
    }

  
}
+89 −17
Original line number Diff line number Diff line
@@ -70,7 +70,10 @@ package ca.nrc.cadc.ac.server.web.users;

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;
@@ -79,8 +82,10 @@ 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.GroupPersistence;
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;
@@ -99,6 +104,8 @@ public class LoginServlet extends HttpServlet
    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
@@ -171,16 +178,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);
@@ -206,25 +221,82 @@ public class LoginServlet extends HttpServlet
	/**
	 * Checks if user can impersonate another user
	 */
    private void checkCanImpersonate(final String userID, final String proxyUser)
    protected void checkCanImpersonate(final String userID, final String proxyUser)
            throws AccessControlException, UserNotFoundException,
            TransientException
            TransientException, Throwable
    {
        GroupPersistence<HttpPrincipal> gp = new LdapGroupPersistence<HttpPrincipal>();
        final LdapGroupPersistence<HttpPrincipal> gp = 
                getLdapGroupPersistence();
        
        
        if (!gp.isMember(new HttpPrincipal(proxyUser), proxyGroup))
        Subject proxySubject = new Subject();
        proxySubject.getPrincipals().add(new HttpPrincipal(proxyUser));
        try
        {
            Subject.doAs(proxySubject, new PrivilegedExceptionAction<Object>()
            {
                @Override
                public Object run() throws Exception
                {
            throw new AccessControlException(proxyUser + " as " + userID
                    + " failed: not in proxyGroup");
                    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;
                }
            });

        if (gp.isMember(new HttpPrincipal(userID), nonImpersonGroup))
            Subject userSubject = new Subject();
            userSubject.getPrincipals().add(new HttpPrincipal(userID));
            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)
        {
            if (gp.isMember(new HttpPrincipal(proxyUser), proxyGroup))
            Throwable cause = e.getCause();
            if (cause != null)
            {
                throw cause;
            }
            Exception exception = e.getException();
            if (exception != null)
            {
                throw new AccessControlException(proxyUser + " as " + userID
                        + " failed: non impersonable");
                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;
    }
}
+106 −0
Original line number Diff line number Diff line
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));
                LdapGroupPersistence<HttpPrincipal> mockGp = 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"));
        }
    }
}