/*
* 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.ambari.server.serveraction.kerberos;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.anyString;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.newCapture;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.security.credential.PrincipalKeyCredential;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.utils.ShellCommandUtil;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IAnswer;
import org.easymock.IMockBuilder;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import junit.framework.Assert;
public class MITKerberosOperationHandlerTest extends KerberosOperationHandlerTest {
private static final String DEFAULT_ADMIN_PRINCIPAL = "admin/admin";
private static final String DEFAULT_ADMIN_PASSWORD = "hadoop";
private static final String DEFAULT_REALM = "EXAMPLE.COM";
private static Injector injector;
private static Method methodExecuteCommand;
private static final Map<String, String> KERBEROS_ENV_MAP = new HashMap<String, String>() {
{
put(MITKerberosOperationHandler.KERBEROS_ENV_ENCRYPTION_TYPES, null);
put(MITKerberosOperationHandler.KERBEROS_ENV_KDC_HOSTS, "localhost");
put(MITKerberosOperationHandler.KERBEROS_ENV_ADMIN_SERVER_HOST, "localhost");
put(MITKerberosOperationHandler.KERBEROS_ENV_AD_CREATE_ATTRIBUTES_TEMPLATE, "AD Create Template");
put(MITKerberosOperationHandler.KERBEROS_ENV_KDC_CREATE_ATTRIBUTES, "-attr1 -attr2 foo=345");
}
};
@BeforeClass
public static void beforeClass() throws Exception {
injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
Configuration configuration = EasyMock.createNiceMock(Configuration.class);
expect(configuration.getServerOsFamily()).andReturn("redhat6").anyTimes();
expect(configuration.getKerberosOperationRetryTimeout()).andReturn(1).anyTimes();
replay(configuration);
bind(Clusters.class).toInstance(EasyMock.createNiceMock(Clusters.class));
bind(Configuration.class).toInstance(configuration);
bind(OsFamily.class).toInstance(EasyMock.createNiceMock(OsFamily.class));
}
});
methodExecuteCommand = KerberosOperationHandler.class.getDeclaredMethod(
"executeCommand",
String[].class,
Map.class,
ShellCommandUtil.InteractiveHandler.class);
}
@Test
public void testSetPrincipalPasswordExceptions() throws Exception {
MITKerberosOperationHandler handler = injector.getInstance(MITKerberosOperationHandler.class);
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
try {
handler.setPrincipalPassword(DEFAULT_ADMIN_PRINCIPAL, null);
Assert.fail("KerberosOperationException not thrown for null password");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
try {
handler.setPrincipalPassword(DEFAULT_ADMIN_PRINCIPAL, "");
Assert.fail("KerberosOperationException not thrown for empty password");
handler.createPrincipal("", "1234", false);
Assert.fail("AmbariException not thrown for empty principal");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
try {
handler.setPrincipalPassword(null, DEFAULT_ADMIN_PASSWORD);
Assert.fail("KerberosOperationException not thrown for null principal");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
try {
handler.setPrincipalPassword("", DEFAULT_ADMIN_PASSWORD);
Assert.fail("KerberosOperationException not thrown for empty principal");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
}
@Test(expected = KerberosPrincipalDoesNotExistException.class)
public void testSetPrincipalPasswordPrincipalDoesNotExist() throws Exception {
MITKerberosOperationHandler handler = createMockBuilder(MITKerberosOperationHandler.class)
.addMockedMethod(methodExecuteCommand)
.createNiceMock();
injector.injectMembers(handler);
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(0).anyTimes();
expect(result.isSuccessful()).andReturn(true).anyTimes();
expect(result.getStderr())
.andReturn("change_password: Principal does not exist while changing password for \"nonexistant@EXAMPLE.COM\".")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.setPrincipalPassword("nonexistant@EXAMPLE.COM", "password");
handler.close();
}
@Test
public void testCreateServicePrincipal_AdditionalAttributes() throws Exception {
Method invokeKAdmin = MITKerberosOperationHandler.class.getDeclaredMethod("invokeKAdmin", String.class, String.class);
Capture<? extends String> query = newCapture();
Capture<? extends String> password = newCapture();
ShellCommandUtil.Result result1 = createNiceMock(ShellCommandUtil.Result.class);
expect(result1.getStderr()).andReturn("").anyTimes();
expect(result1.getStdout()).andReturn("Principal \"" + DEFAULT_ADMIN_PRINCIPAL + "\" created\"").anyTimes();
ShellCommandUtil.Result result2 = createNiceMock(ShellCommandUtil.Result.class);
expect(result2.getStderr()).andReturn("").anyTimes();
expect(result2.getStdout()).andReturn("Key: vno 1").anyTimes();
MITKerberosOperationHandler handler = createMockBuilder(MITKerberosOperationHandler.class)
.addMockedMethod(invokeKAdmin)
.createStrictMock();
expect(handler.invokeKAdmin(capture(query), anyString())).andReturn(result1).once();
expect(handler.invokeKAdmin("get_principal " + DEFAULT_ADMIN_PRINCIPAL, null)).andReturn(result2).once();
replay(handler, result1, result2);
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD, false);
verify(handler, result1, result2);
Assert.assertTrue(query.getValue().contains(" " + KERBEROS_ENV_MAP.get(MITKerberosOperationHandler.KERBEROS_ENV_KDC_CREATE_ATTRIBUTES) + " "));
}
@Test(expected = KerberosPrincipalAlreadyExistsException.class)
public void testCreatePrincipalPrincipalAlreadyNotExists() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(0).anyTimes();
expect(result.isSuccessful()).andReturn(true).anyTimes();
expect(result.getStderr())
.andReturn("add_principal: Principal or policy already exists while creating \"existing@EXAMPLE.COM\".")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.createPrincipal("existing@EXAMPLE.COM", "password", false);
handler.close();
}
@Test
public void testCreateServicePrincipal_Exceptions() throws Exception {
MITKerberosOperationHandler handler = new MITKerberosOperationHandler();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
try {
handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, null, false);
Assert.fail("KerberosOperationException not thrown for null password");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
try {
handler.createPrincipal(DEFAULT_ADMIN_PRINCIPAL, "", false);
Assert.fail("KerberosOperationException not thrown for empty password");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
try {
handler.createPrincipal(null, DEFAULT_ADMIN_PASSWORD, false);
Assert.fail("KerberosOperationException not thrown for null principal");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
try {
handler.createPrincipal("", DEFAULT_ADMIN_PASSWORD, false);
Assert.fail("KerberosOperationException not thrown for empty principal");
} catch (Throwable t) {
Assert.assertEquals(KerberosOperationException.class, t.getClass());
}
}
@Test(expected = KerberosAdminAuthenticationException.class)
public void testTestAdministratorCredentialsIncorrectAdminPassword() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(1).anyTimes();
expect(result.isSuccessful()).andReturn(false).anyTimes();
expect(result.getStderr())
.andReturn("kadmin: Incorrect password while initializing kadmin interface")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test(expected = KerberosAdminAuthenticationException.class)
public void testTestAdministratorCredentialsIncorrectAdminPrincipal() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(1).anyTimes();
expect(result.isSuccessful()).andReturn(false).anyTimes();
expect(result.getStderr())
.andReturn("kadmin: Client not found in Kerberos database while initializing kadmin interface")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test(expected = KerberosRealmException.class)
public void testTestAdministratorCredentialsInvalidRealm() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(1).anyTimes();
expect(result.isSuccessful()).andReturn(false).anyTimes();
expect(result.getStderr())
.andReturn("kadmin: Missing parameters in krb5.conf required for kadmin client while initializing kadmin interface")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test(expected = KerberosRealmException.class)
public void testTestAdministratorCredentialsInvalidRealm2() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(1).anyTimes();
expect(result.isSuccessful()).andReturn(false).anyTimes();
expect(result.getStderr())
.andReturn("kadmin: Cannot find KDC for requested realm while initializing kadmin interface")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test(expected = KerberosKDCConnectionException.class)
public void testTestAdministratorCredentialsKDCConnectionException() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(1).anyTimes();
expect(result.isSuccessful()).andReturn(false).anyTimes();
expect(result.getStderr())
.andReturn("kadmin: Cannot contact any KDC for requested realm while initializing kadmin interface")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test(expected = KerberosKDCConnectionException.class)
public void testTestAdministratorCredentialsKDCConnectionException2() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(1).anyTimes();
expect(result.isSuccessful()).andReturn(false).anyTimes();
expect(result.getStderr())
.andReturn("kadmin: Cannot resolve network address for admin server in requested realm while initializing kadmin interface")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test
public void testTestAdministratorCredentialsNotFound() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(0).anyTimes();
expect(result.isSuccessful()).andReturn(true).anyTimes();
expect(result.getStderr())
.andReturn("get_principal: Principal does not exist while retrieving \"admin/admi@EXAMPLE.COM\".")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
Assert.assertFalse(handler.testAdministratorCredentials());
handler.close();
}
@Test
public void testTestAdministratorCredentialsSuccess() throws Exception {
MITKerberosOperationHandler handler = createMock();
expect(handler.executeCommand(anyObject(String[].class), EasyMock.<Map<String, String>>anyObject(), anyObject(MITKerberosOperationHandler.InteractivePasswordHandler.class)))
.andAnswer(new IAnswer<ShellCommandUtil.Result>() {
@Override
public ShellCommandUtil.Result answer() throws Throwable {
ShellCommandUtil.Result result = createMock(ShellCommandUtil.Result.class);
expect(result.getExitCode()).andReturn(0).anyTimes();
expect(result.isSuccessful()).andReturn(true).anyTimes();
expect(result.getStderr())
.andReturn("")
.anyTimes();
expect(result.getStdout())
.andReturn("Authenticating as principal admin/admin with password.\n" +
"Principal: admin/admin@EXAMPLE.COM\n" +
"Expiration date: [never]\n" +
"Last password change: Thu Jan 08 13:09:52 UTC 2015\n" +
"Password expiration date: [none]\n" +
"Maximum ticket life: 1 day 00:00:00\n" +
"Maximum renewable life: 0 days 00:00:00\n" +
"Last modified: Thu Jan 08 13:09:52 UTC 2015 (root/admin@EXAMPLE.COM)\n" +
"Last successful authentication: [never]\n" +
"Last failed authentication: [never]\n" +
"Failed password attempts: 0\n" +
"Number of keys: 6\n" +
"Key: vno 1, aes256-cts-hmac-sha1-96, no salt\n" +
"Key: vno 1, aes128-cts-hmac-sha1-96, no salt\n" +
"Key: vno 1, des3-cbc-sha1, no salt\n" +
"Key: vno 1, arcfour-hmac, no salt\n" +
"Key: vno 1, des-hmac-sha1, no salt\n" +
"Key: vno 1, des-cbc-md5, no salt\n" +
"MKey: vno 1\n" +
"Attributes:\n" +
"Policy: [none]")
.anyTimes();
replay(result);
return result;
}
});
replayAll();
handler.open(new PrincipalKeyCredential(DEFAULT_ADMIN_PRINCIPAL, DEFAULT_ADMIN_PASSWORD), DEFAULT_REALM, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test
@Ignore
public void testTestAdministratorCredentialsLive() throws KerberosOperationException {
MITKerberosOperationHandler handler = new MITKerberosOperationHandler();
String principal = System.getProperty("principal");
String password = System.getProperty("password");
String realm = System.getProperty("realm");
if (principal == null) {
principal = DEFAULT_ADMIN_PRINCIPAL;
}
if (password == null) {
password = DEFAULT_ADMIN_PASSWORD;
}
if (realm == null) {
realm = DEFAULT_REALM;
}
PrincipalKeyCredential credentials = new PrincipalKeyCredential(principal, password);
handler.open(credentials, realm, KERBEROS_ENV_MAP);
handler.testAdministratorCredentials();
handler.close();
}
@Test
public void testInteractivePasswordHandler() {
MITKerberosOperationHandler.InteractivePasswordHandler handler = new MITKerberosOperationHandler.InteractivePasswordHandler("admin_password", "user_password");
handler.start();
Assert.assertEquals("admin_password", handler.getResponse("password"));
Assert.assertFalse(handler.done());
Assert.assertEquals("user_password", handler.getResponse("password"));
Assert.assertFalse(handler.done());
Assert.assertEquals("user_password", handler.getResponse("password"));
Assert.assertTrue(handler.done());
// Test restarting
handler.start();
Assert.assertEquals("admin_password", handler.getResponse("password"));
Assert.assertFalse(handler.done());
Assert.assertEquals("user_password", handler.getResponse("password"));
Assert.assertFalse(handler.done());
Assert.assertEquals("user_password", handler.getResponse("password"));
Assert.assertTrue(handler.done());
}
private MITKerberosOperationHandler createMock(){
return createMock(false);
}
private MITKerberosOperationHandler createMock(boolean strict) {
IMockBuilder<MITKerberosOperationHandler> mockBuilder = createMockBuilder(MITKerberosOperationHandler.class)
.addMockedMethod(methodExecuteCommand);
MITKerberosOperationHandler result;
if(strict){
result = mockBuilder.createStrictMock();
} else {
result = mockBuilder.createNiceMock();
}
injector.injectMembers(result);
return result;
}
}