HADOOP-8458. Add management hook to AuthenticationHandler to enable delegation token operations support (tucu)

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1349514 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Alejandro Abdelnur 2012-06-12 20:45:57 +00:00
parent f9c938666b
commit 22c6588623
6 changed files with 226 additions and 50 deletions

View File

@ -341,45 +341,49 @@ public class AuthenticationFilter implements Filter {
LOG.warn("AuthenticationToken ignored: " + ex.getMessage()); LOG.warn("AuthenticationToken ignored: " + ex.getMessage());
token = null; token = null;
} }
if (token == null) { if (authHandler.managementOperation(token, httpRequest, httpResponse)) {
if (LOG.isDebugEnabled()) { if (token == null) {
LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest)); if (LOG.isDebugEnabled()) {
LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest));
}
token = authHandler.authenticate(httpRequest, httpResponse);
if (token != null && token != AuthenticationToken.ANONYMOUS) {
token.setExpires(System.currentTimeMillis() + getValidity() * 1000);
}
newToken = true;
} }
token = authHandler.authenticate(httpRequest, httpResponse); if (token != null) {
if (token != null && token != AuthenticationToken.ANONYMOUS) { unauthorizedResponse = false;
token.setExpires(System.currentTimeMillis() + getValidity() * 1000); if (LOG.isDebugEnabled()) {
LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName());
}
final AuthenticationToken authToken = token;
httpRequest = new HttpServletRequestWrapper(httpRequest) {
@Override
public String getAuthType() {
return authToken.getType();
}
@Override
public String getRemoteUser() {
return authToken.getUserName();
}
@Override
public Principal getUserPrincipal() {
return (authToken != AuthenticationToken.ANONYMOUS) ? authToken : null;
}
};
if (newToken && token != AuthenticationToken.ANONYMOUS) {
String signedToken = signer.sign(token.toString());
Cookie cookie = createCookie(signedToken);
httpResponse.addCookie(cookie);
}
filterChain.doFilter(httpRequest, httpResponse);
} }
newToken = true; } else {
}
if (token != null) {
unauthorizedResponse = false; unauthorizedResponse = false;
if (LOG.isDebugEnabled()) {
LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName());
}
final AuthenticationToken authToken = token;
httpRequest = new HttpServletRequestWrapper(httpRequest) {
@Override
public String getAuthType() {
return authToken.getType();
}
@Override
public String getRemoteUser() {
return authToken.getUserName();
}
@Override
public Principal getUserPrincipal() {
return (authToken != AuthenticationToken.ANONYMOUS) ? authToken : null;
}
};
if (newToken && token != AuthenticationToken.ANONYMOUS) {
String signedToken = signer.sign(token.toString());
Cookie cookie = createCookie(signedToken);
httpResponse.addCookie(cookie);
}
filterChain.doFilter(httpRequest, httpResponse);
} }
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
unauthorizedMsg = ex.toString(); unauthorizedMsg = ex.toString();

View File

@ -58,6 +58,34 @@ public interface AuthenticationHandler {
*/ */
public void destroy(); public void destroy();
/**
* Performs an authentication management operation.
* <p/>
* This is useful for handling operations like get/renew/cancel
* delegation tokens which are being handled as operations of the
* service end-point.
* <p/>
* If the method returns <code>TRUE</code> the request will continue normal
* processing, this means the method has not produced any HTTP response.
* <p/>
* If the method returns <code>FALSE</code> the request will end, this means
* the method has produced the corresponding HTTP response.
*
* @param token the authentication token if any, otherwise <code>NULL</code>.
* @param request the HTTP client request.
* @param response the HTTP client response.
* @return <code>TRUE</code> if the request should be processed as a regular
* request,
* <code>FALSE</code> otherwise.
*
* @throws IOException thrown if an IO error occurred.
* @throws AuthenticationException thrown if an Authentication error occurred.
*/
public boolean managementOperation(AuthenticationToken token,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, AuthenticationException;
/** /**
* Performs an authentication step for the given HTTP client request. * Performs an authentication step for the given HTTP client request.
* <p/> * <p/>

View File

@ -232,6 +232,27 @@ public class KerberosAuthenticationHandler implements AuthenticationHandler {
return keytab; return keytab;
} }
/**
* This is an empty implementation, it always returns <code>TRUE</code>.
*
*
*
* @param token the authentication token if any, otherwise <code>NULL</code>.
* @param request the HTTP client request.
* @param response the HTTP client response.
*
* @return <code>TRUE</code>
* @throws IOException it is never thrown.
* @throws AuthenticationException it is never thrown.
*/
@Override
public boolean managementOperation(AuthenticationToken token,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, AuthenticationException {
return true;
}
/** /**
* It enforces the the Kerberos SPNEGO authentication sequence returning an {@link AuthenticationToken} only * It enforces the the Kerberos SPNEGO authentication sequence returning an {@link AuthenticationToken} only
* after the Kerberos SPNEGO sequence has completed successfully. * after the Kerberos SPNEGO sequence has completed successfully.

View File

@ -93,6 +93,27 @@ public class PseudoAuthenticationHandler implements AuthenticationHandler {
return TYPE; return TYPE;
} }
/**
* This is an empty implementation, it always returns <code>TRUE</code>.
*
*
*
* @param token the authentication token if any, otherwise <code>NULL</code>.
* @param request the HTTP client request.
* @param response the HTTP client response.
*
* @return <code>TRUE</code>
* @throws IOException it is never thrown.
* @throws AuthenticationException it is never thrown.
*/
@Override
public boolean managementOperation(AuthenticationToken token,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, AuthenticationException {
return true;
}
/** /**
* Authenticates an HTTP client request. * Authenticates an HTTP client request.
* <p/> * <p/>

View File

@ -71,6 +71,7 @@ public class TestAuthenticationFilter extends TestCase {
public static class DummyAuthenticationHandler implements AuthenticationHandler { public static class DummyAuthenticationHandler implements AuthenticationHandler {
public static boolean init; public static boolean init;
public static boolean managementOperationReturn;
public static boolean destroy; public static boolean destroy;
public static final String TYPE = "dummy"; public static final String TYPE = "dummy";
@ -83,6 +84,19 @@ public class TestAuthenticationFilter extends TestCase {
@Override @Override
public void init(Properties config) throws ServletException { public void init(Properties config) throws ServletException {
init = true; init = true;
managementOperationReturn =
config.getProperty("management.operation.return", "true").equals("true");
}
@Override
public boolean managementOperation(AuthenticationToken token,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, AuthenticationException {
if (!managementOperationReturn) {
response.setStatus(HttpServletResponse.SC_ACCEPTED);
}
return managementOperationReturn;
} }
@Override @Override
@ -170,10 +184,14 @@ public class TestAuthenticationFilter extends TestCase {
filter = new AuthenticationFilter(); filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE)).elements()); new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
assertTrue(DummyAuthenticationHandler.init); assertTrue(DummyAuthenticationHandler.init);
} finally { } finally {
@ -201,10 +219,14 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE)).elements()); new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@ -221,12 +243,16 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret"); Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret");
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE, new Vector<String>(
AuthenticationFilter.SIGNATURE_SECRET)).elements()); Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
AuthenticationToken token = new AuthenticationToken("u", "p", DummyAuthenticationHandler.TYPE); AuthenticationToken token = new AuthenticationToken("u", "p", DummyAuthenticationHandler.TYPE);
@ -250,12 +276,15 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret"); Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret");
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE, new Vector<String>(
AuthenticationFilter.SIGNATURE_SECRET)).elements()); Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype"); AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype");
@ -284,12 +313,16 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret"); Mockito.when(config.getInitParameter(AuthenticationFilter.SIGNATURE_SECRET)).thenReturn("secret");
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE, new Vector<String>(
AuthenticationFilter.SIGNATURE_SECRET)).elements()); Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype"); AuthenticationToken token = new AuthenticationToken("u", "p", "invalidtype");
@ -318,10 +351,14 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE)).elements()); new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@ -353,6 +390,8 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TOKEN_VALIDITY)).thenReturn("1000"); Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TOKEN_VALIDITY)).thenReturn("1000");
@ -360,7 +399,8 @@ public class TestAuthenticationFilter extends TestCase {
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE, new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE,
AuthenticationFilter.AUTH_TOKEN_VALIDITY, AuthenticationFilter.AUTH_TOKEN_VALIDITY,
AuthenticationFilter.SIGNATURE_SECRET)).elements()); AuthenticationFilter.SIGNATURE_SECRET,
"management.operation.return")).elements());
if (withDomainPath) { if (withDomainPath) {
Mockito.when(config.getInitParameter(AuthenticationFilter.COOKIE_DOMAIN)).thenReturn(".foo.com"); Mockito.when(config.getInitParameter(AuthenticationFilter.COOKIE_DOMAIN)).thenReturn(".foo.com");
@ -370,7 +410,8 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter.AUTH_TOKEN_VALIDITY, AuthenticationFilter.AUTH_TOKEN_VALIDITY,
AuthenticationFilter.SIGNATURE_SECRET, AuthenticationFilter.SIGNATURE_SECRET,
AuthenticationFilter.COOKIE_DOMAIN, AuthenticationFilter.COOKIE_DOMAIN,
AuthenticationFilter.COOKIE_PATH)).elements()); AuthenticationFilter.COOKIE_PATH,
"management.operation.return")).elements());
} }
filter.init(config); filter.init(config);
@ -458,10 +499,14 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE)).elements()); new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@ -503,10 +548,14 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE)).elements()); new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@ -563,10 +612,14 @@ public class TestAuthenticationFilter extends TestCase {
AuthenticationFilter filter = new AuthenticationFilter(); AuthenticationFilter filter = new AuthenticationFilter();
try { try {
FilterConfig config = Mockito.mock(FilterConfig.class); FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("true");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn( Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).thenReturn(
DummyAuthenticationHandler.class.getName()); DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn( Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(Arrays.asList(AuthenticationFilter.AUTH_TYPE)).elements()); new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config); filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class); HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
@ -618,4 +671,50 @@ public class TestAuthenticationFilter extends TestCase {
} }
} }
public void testManagementOperation() throws Exception {
AuthenticationFilter filter = new AuthenticationFilter();
try {
FilterConfig config = Mockito.mock(FilterConfig.class);
Mockito.when(config.getInitParameter("management.operation.return")).
thenReturn("false");
Mockito.when(config.getInitParameter(AuthenticationFilter.AUTH_TYPE)).
thenReturn(DummyAuthenticationHandler.class.getName());
Mockito.when(config.getInitParameterNames()).thenReturn(
new Vector<String>(
Arrays.asList(AuthenticationFilter.AUTH_TYPE,
"management.operation.return")).elements());
filter.init(config);
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
Mockito.when(request.getRequestURL()).
thenReturn(new StringBuffer("http://foo:8080/bar"));
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(request, response, chain);
Mockito.verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
Mockito.verifyNoMoreInteractions(response);
Mockito.reset(request);
Mockito.reset(response);
AuthenticationToken token = new AuthenticationToken("u", "p", "t");
token.setExpires(System.currentTimeMillis() + 1000);
Signer signer = new Signer("secret".getBytes());
String tokenSigned = signer.sign(token.toString());
Cookie cookie = new Cookie(AuthenticatedURL.AUTH_COOKIE, tokenSigned);
Mockito.when(request.getCookies()).thenReturn(new Cookie[]{cookie});
filter.doFilter(request, response, chain);
Mockito.verify(response).setStatus(HttpServletResponse.SC_ACCEPTED);
Mockito.verifyNoMoreInteractions(response);
} finally {
filter.destroy();
}
}
} }

View File

@ -174,6 +174,9 @@ Branch-2 ( Unreleased changes )
HADOOP-8135. Add ByteBufferReadable interface to FSDataInputStream. (Henry HADOOP-8135. Add ByteBufferReadable interface to FSDataInputStream. (Henry
Robinson via atm) Robinson via atm)
HADOOP-8458. Add management hook to AuthenticationHandler to enable
delegation token operations support (tucu)
IMPROVEMENTS IMPROVEMENTS
HADOOP-8340. SNAPSHOT build versions should compare as less than their eventual HADOOP-8340. SNAPSHOT build versions should compare as less than their eventual