/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flume.auth;
import org.apache.hadoop.minikdc.MiniKdc;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class TestFlumeAuthenticator {
private static MiniKdc kdc;
private static File workDir;
private static File flumeKeytab;
private static String flumePrincipal = "flume/localhost";
private static File aliceKeytab;
private static String alicePrincipal = "alice";
private static Properties conf;
@BeforeClass
public static void startMiniKdc() throws Exception {
workDir = new File(System.getProperty("test.dir", "target"),
TestFlumeAuthenticator.class.getSimpleName());
flumeKeytab = new File(workDir, "flume.keytab");
aliceKeytab = new File(workDir, "alice.keytab");
conf = MiniKdc.createConf();
kdc = new MiniKdc(conf, workDir);
kdc.start();
kdc.createPrincipal(flumeKeytab, flumePrincipal);
flumePrincipal = flumePrincipal + "@" + kdc.getRealm();
kdc.createPrincipal(aliceKeytab, alicePrincipal);
alicePrincipal = alicePrincipal + "@" + kdc.getRealm();
}
@AfterClass
public static void stopMiniKdc() {
if (kdc != null) {
kdc.stop();
}
}
@After
public void tearDown() {
// Clear the previous statically stored logged in credentials
FlumeAuthenticationUtil.clearCredentials();
}
@Test
public void testNullLogin() throws IOException {
String principal = null;
String keytab = null;
FlumeAuthenticator authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab);
assertFalse(authenticator.isAuthenticated());
}
@Test
public void testFlumeLogin() throws IOException {
String principal = flumePrincipal;
String keytab = flumeKeytab.getAbsolutePath();
String expResult = principal;
FlumeAuthenticator authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab);
assertTrue(authenticator.isAuthenticated());
String result = ((KerberosAuthenticator)authenticator).getUserName();
assertEquals("Initial login failed", expResult, result);
authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab);
result = ((KerberosAuthenticator)authenticator).getUserName();
assertEquals("Re-login failed", expResult, result);
principal = alicePrincipal;
keytab = aliceKeytab.getAbsolutePath();
try {
authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab);
result = ((KerberosAuthenticator)authenticator).getUserName();
fail("Login should have failed with a new principal: " + result);
} catch (Exception ex) {
assertTrue("Login with a new principal failed, but for an unexpected "
+ "reason: " + ex.getMessage(),
ex.getMessage().contains("Cannot use multiple kerberos principals"));
}
}
/**
* Test whether the exception raised in the <code>PrivilegedExceptionAction</code> gets
* propagated as-is from {@link KerberosAuthenticator#execute(PrivilegedExceptionAction)}.
*/
@Test(expected = IOException.class)
public void testKerberosAuthenticatorExceptionInExecute() throws Exception {
String principal = flumePrincipal;
String keytab = flumeKeytab.getAbsolutePath();
FlumeAuthenticator authenticator = FlumeAuthenticationUtil.getAuthenticator(principal, keytab);
assertTrue(authenticator instanceof KerberosAuthenticator);
authenticator.execute(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
throw new IOException();
}
});
}
/**
* Test whether the exception raised in the <code>PrivilegedExceptionAction</code> gets
* propagated as-is from {@link SimpleAuthenticator#execute(PrivilegedExceptionAction)}.
*/
@Test(expected = IOException.class)
public void testSimpleAuthenticatorExceptionInExecute() throws Exception {
FlumeAuthenticator authenticator = FlumeAuthenticationUtil.getAuthenticator(null, null);
assertTrue(authenticator instanceof SimpleAuthenticator);
authenticator.execute(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
throw new IOException();
}
});
}
@Test
public void testProxyAs() throws IOException {
String username = "alice";
String expResult = username;
FlumeAuthenticator authenticator = FlumeAuthenticationUtil.getAuthenticator(
null, null);
String result = ((UGIExecutor)(authenticator.proxyAs(username))).getUserName();
assertEquals("Proxy as didn't generate the expected username", expResult, result);
authenticator = FlumeAuthenticationUtil.getAuthenticator(
flumePrincipal, flumeKeytab.getAbsolutePath());
String login = ((KerberosAuthenticator)authenticator).getUserName();
assertEquals("Login succeeded, but the principal doesn't match",
flumePrincipal, login);
result = ((UGIExecutor)(authenticator.proxyAs(username))).getUserName();
assertEquals("Proxy as didn't generate the expected username", expResult, result);
}
@Test
public void testFlumeLoginPrincipalWithoutRealm() throws Exception {
String principal = "flume";
File keytab = new File(workDir, "flume2.keytab");
kdc.createPrincipal(keytab, principal);
String expResult = principal + "@" + kdc.getRealm();
FlumeAuthenticator authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab.getAbsolutePath());
assertTrue(authenticator.isAuthenticated());
String result = ((KerberosAuthenticator)authenticator).getUserName();
assertEquals("Initial login failed", expResult, result);
authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab.getAbsolutePath());
result = ((KerberosAuthenticator)authenticator).getUserName();
assertEquals("Re-login failed", expResult, result);
principal = "alice";
keytab = aliceKeytab;
try {
authenticator = FlumeAuthenticationUtil.getAuthenticator(
principal, keytab.getAbsolutePath());
result = ((KerberosAuthenticator)authenticator).getUserName();
fail("Login should have failed with a new principal: " + result);
} catch (Exception ex) {
assertTrue("Login with a new principal failed, but for an unexpected "
+ "reason: " + ex.getMessage(),
ex.getMessage().contains("Cannot use multiple kerberos principals"));
}
}
}