NIFI-5945 Add support for password login to kerberos code in nifi-security-utils

Fixing solr test

Signed-off-by: Matthew Burgess <mattyb149@apache.org>

This closes #3256
This commit is contained in:
Bryan Bende 2019-01-09 17:37:10 -05:00 committed by Matthew Burgess
parent cf7ab0ce18
commit 2bbfb3217b
12 changed files with 320 additions and 112 deletions

View File

@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory;
import javax.security.auth.Subject; import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException; import javax.security.auth.login.LoginException;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
@ -34,14 +33,9 @@ import java.util.Date;
import java.util.Set; import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
/** public abstract class AbstractKerberosUser implements KerberosUser {
* Used to authenticate and execute actions when Kerberos is enabled and a keytab is being used.
*
* Some of the functionality in this class is adapted from Hadoop's UserGroupInformation.
*/
public class StandardKeytabUser implements KeytabUser {
private static final Logger LOGGER = LoggerFactory.getLogger(StandardKeytabUser.class); private static final Logger LOGGER = LoggerFactory.getLogger(AbstractKerberosUser.class);
static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
@ -50,18 +44,15 @@ public class StandardKeytabUser implements KeytabUser {
*/ */
static final float TICKET_RENEW_WINDOW = 0.80f; static final float TICKET_RENEW_WINDOW = 0.80f;
private final String principal; protected final String principal;
private final String keytabFile; protected final AtomicBoolean loggedIn = new AtomicBoolean(false);
private final AtomicBoolean loggedIn = new AtomicBoolean(false);
private Subject subject; protected Subject subject;
private LoginContext loginContext; protected LoginContext loginContext;
public StandardKeytabUser(final String principal, final String keytabFile) { public AbstractKerberosUser(final String principal) {
this.principal = principal; this.principal = principal;
this.keytabFile = keytabFile; Validate.notBlank(this.principal);
Validate.notBlank(principal);
Validate.notBlank(keytabFile);
} }
/** /**
@ -80,19 +71,19 @@ public class StandardKeytabUser implements KeytabUser {
if (loginContext == null) { if (loginContext == null) {
LOGGER.debug("Initializing new login context..."); LOGGER.debug("Initializing new login context...");
this.subject = new Subject(); this.subject = new Subject();
this.loginContext = createLoginContext(subject);
final Configuration config = new KeytabConfiguration(principal, keytabFile);
this.loginContext = new LoginContext("KeytabConf", subject, null, config);
} }
loginContext.login(); loginContext.login();
loggedIn.set(true); loggedIn.set(true);
LOGGER.debug("Successful login for {}", new Object[]{principal}); LOGGER.debug("Successful login for {}", new Object[]{principal});
} catch (LoginException le) { } catch (LoginException le) {
throw new LoginException("Unable to login with " + principal + " and " + keytabFile + " due to: " + le.getMessage()); throw new LoginException("Unable to login with " + principal + " due to: " + le.getMessage());
} }
} }
protected abstract LoginContext createLoginContext(final Subject subject) throws LoginException;
/** /**
* Performs a logout of the current user. * Performs a logout of the current user.
* *
@ -244,14 +235,6 @@ public class StandardKeytabUser implements KeytabUser {
return principal; return principal;
} }
/**
* @return the keytab file for this user
*/
@Override
public String getKeytabFile() {
return keytabFile;
}
// Visible for testing // Visible for testing
Subject getSubject() { Subject getSubject() {
return this.subject; return this.subject;

View File

@ -0,0 +1,25 @@
/*
* 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.nifi.security.krb;
public interface ConfigurationUtil {
boolean IS_IBM = System.getProperty("java.vendor", "").contains("IBM");
String IBM_KRB5_LOGIN_MODULE = "com.ibm.security.auth.module.Krb5LoginModule";
String SUN_KRB5_LOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule";
}

View File

@ -25,24 +25,24 @@ import javax.security.auth.login.LoginException;
import java.security.PrivilegedAction; import java.security.PrivilegedAction;
/** /**
* Helper class for processors to perform an action as a KeytabUser. * Helper class for processors to perform an action as a KerberosUser.
*/ */
public class KeytabAction { public class KerberosAction {
private final KeytabUser keytabUser; private final KerberosUser kerberosUser;
private final PrivilegedAction action; private final PrivilegedAction action;
private final ProcessContext context; private final ProcessContext context;
private final ComponentLog logger; private final ComponentLog logger;
public KeytabAction(final KeytabUser keytabUser, public KerberosAction(final KerberosUser kerberosUser,
final PrivilegedAction action, final PrivilegedAction action,
final ProcessContext context, final ProcessContext context,
final ComponentLog logger) { final ComponentLog logger) {
this.keytabUser = keytabUser; this.kerberosUser = kerberosUser;
this.action = action; this.action = action;
this.context = context; this.context = context;
this.logger = logger; this.logger = logger;
Validate.notNull(this.keytabUser); Validate.notNull(this.kerberosUser);
Validate.notNull(this.action); Validate.notNull(this.action);
Validate.notNull(this.context); Validate.notNull(this.context);
Validate.notNull(this.logger); Validate.notNull(this.logger);
@ -50,10 +50,10 @@ public class KeytabAction {
public void execute() { public void execute() {
// lazily login the first time the processor executes // lazily login the first time the processor executes
if (!keytabUser.isLoggedIn()) { if (!kerberosUser.isLoggedIn()) {
try { try {
keytabUser.login(); kerberosUser.login();
logger.info("Successful login for {}", new Object[]{keytabUser.getPrincipal()}); logger.info("Successful login for {}", new Object[]{kerberosUser.getPrincipal()});
} catch (LoginException e) { } catch (LoginException e) {
// make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately // make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately
context.yield(); context.yield();
@ -63,7 +63,7 @@ public class KeytabAction {
// check if we need to re-login, will only happen if re-login window is reached (80% of TGT life) // check if we need to re-login, will only happen if re-login window is reached (80% of TGT life)
try { try {
keytabUser.checkTGTAndRelogin(); kerberosUser.checkTGTAndRelogin();
} catch (LoginException e) { } catch (LoginException e) {
// make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately // make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately
context.yield(); context.yield();
@ -72,15 +72,15 @@ public class KeytabAction {
// attempt to execute the action, if an exception is caught attempt to logout/login and retry // attempt to execute the action, if an exception is caught attempt to logout/login and retry
try { try {
keytabUser.doAs(action); kerberosUser.doAs(action);
} catch (SecurityException se) { } catch (SecurityException se) {
logger.info("Privileged action failed, attempting relogin and retrying..."); logger.info("Privileged action failed, attempting relogin and retrying...");
logger.debug("", se); logger.debug("", se);
try { try {
keytabUser.logout(); kerberosUser.logout();
keytabUser.login(); kerberosUser.login();
keytabUser.doAs(action); kerberosUser.doAs(action);
} catch (Exception e) { } catch (Exception e) {
// make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately // make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately
context.yield(); context.yield();

View File

@ -0,0 +1,59 @@
/*
* 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.nifi.security.krb;
import org.apache.commons.lang3.Validate;
import javax.security.auth.Subject;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
/**
* Used to authenticate and execute actions when Kerberos is enabled and a keytab is being used.
*
* Some of the functionality in this class is adapted from Hadoop's UserGroupInformation.
*/
public class KerberosKeytabUser extends AbstractKerberosUser {
private final String keytabFile;
public KerberosKeytabUser(final String principal, final String keytabFile) {
super(principal);
this.keytabFile = keytabFile;
Validate.notBlank(keytabFile);
}
@Override
protected LoginContext createLoginContext(Subject subject) throws LoginException {
final Configuration config = new KeytabConfiguration(principal, keytabFile);
return new LoginContext("KeytabConf", subject, null, config);
}
/**
* @return the keytab file for this user
*/
public String getKeytabFile() {
return keytabFile;
}
// Visible for testing
Subject getSubject() {
return this.subject;
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.nifi.security.krb;
import org.apache.commons.lang3.Validate;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.util.HashMap;
/**
* KerberosUser that authenticates via username and password instead of keytab.
*/
public class KerberosPasswordUser extends AbstractKerberosUser {
private final String password;
public KerberosPasswordUser(final String principal, final String password) {
super(principal);
this.password = password;
Validate.notBlank(this.password);
}
@Override
protected LoginContext createLoginContext(final Subject subject) throws LoginException {
final Configuration configuration = new PasswordConfig();
final CallbackHandler callbackHandler = new UsernamePasswordCallbackHandler(principal, password);
return new LoginContext("PasswordConf", subject, callbackHandler, configuration);
}
/**
* JAAS Configuration to use when logging in with username/password.
*/
private static class PasswordConfig extends Configuration {
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
HashMap<String, String> options = new HashMap<String, String>();
options.put("storeKey", "true");
options.put("refreshKrb5Config", "true");
final String krbLoginModuleName = ConfigurationUtil.IS_IBM
? ConfigurationUtil.IBM_KRB5_LOGIN_MODULE : ConfigurationUtil.SUN_KRB5_LOGIN_MODULE;
return new AppConfigurationEntry[] {
new AppConfigurationEntry(
krbLoginModuleName,
AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options
)
};
}
}
/**
* CallbackHandler that provides the given username and password.
*/
private static class UsernamePasswordCallbackHandler implements CallbackHandler {
private final String username;
private final String password;
public UsernamePasswordCallbackHandler(final String username, final String password) {
this.username = username;
this.password = password;
Validate.notBlank(this.username);
Validate.notBlank(this.password);
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (final Callback callback : callbacks) {
if (callback instanceof NameCallback) {
final NameCallback nameCallback = (NameCallback) callback;
nameCallback.setName(username);
} else if (callback instanceof PasswordCallback) {
final PasswordCallback passwordCallback = (PasswordCallback) callback;
passwordCallback.setPassword(password.toCharArray());
} else {
throw new IllegalStateException("Unexpected callback type: " + callback.getClass().getCanonicalName());
}
}
}
}
}

View File

@ -24,7 +24,7 @@ import java.security.PrivilegedExceptionAction;
/** /**
* A keytab-based user that can login/logout and perform actions as the given user. * A keytab-based user that can login/logout and perform actions as the given user.
*/ */
public interface KeytabUser { public interface KerberosUser {
/** /**
* Performs a login for the given user. * Performs a login for the given user.
@ -80,9 +80,4 @@ public interface KeytabUser {
*/ */
String getPrincipal(); String getPrincipal();
/**
* @return the keytab file for this user
*/
String getKeytabFile();
} }

View File

@ -28,10 +28,6 @@ import java.util.Map;
*/ */
public class KeytabConfiguration extends Configuration { public class KeytabConfiguration extends Configuration {
static final boolean IS_IBM = System.getProperty("java.vendor", "").contains("IBM");
static final String IBM_KRB5_LOGIN_MODULE = "com.ibm.security.auth.module.Krb5LoginModule";
static final String SUN_KRB5_LOGIN_MODULE = "com.sun.security.auth.module.Krb5LoginModule";
private final String principal; private final String principal;
private final String keytabFile; private final String keytabFile;
@ -53,7 +49,7 @@ public class KeytabConfiguration extends Configuration {
options.put("principal", principal); options.put("principal", principal);
options.put("refreshKrb5Config", "true"); options.put("refreshKrb5Config", "true");
if (IS_IBM) { if (ConfigurationUtil.IS_IBM) {
options.put("useKeytab", keytabFile); options.put("useKeytab", keytabFile);
options.put("credsType", "both"); options.put("credsType", "both");
} else { } else {
@ -64,7 +60,8 @@ public class KeytabConfiguration extends Configuration {
options.put("storeKey", "true"); options.put("storeKey", "true");
} }
final String krbLoginModuleName = IS_IBM ? IBM_KRB5_LOGIN_MODULE : SUN_KRB5_LOGIN_MODULE; final String krbLoginModuleName = ConfigurationUtil.IS_IBM
? ConfigurationUtil.IBM_KRB5_LOGIN_MODULE : ConfigurationUtil.SUN_KRB5_LOGIN_MODULE;
this.kerberosKeytabConfigEntry = new AppConfigurationEntry( this.kerberosKeytabConfigEntry = new AppConfigurationEntry(
krbLoginModuleName, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); krbLoginModuleName, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options);

View File

@ -67,8 +67,11 @@ public class KDCServer {
return kdc.getRealm(); return kdc.getRealm();
} }
public void createKeytabFile(final File keytabFile, final String... names) throws Exception { public void createKeytabPrincipal(final File keytabFile, final String... names) throws Exception {
kdc.createPrincipal(keytabFile, names); kdc.createPrincipal(keytabFile, names);
} }
public void createPasswordPrincipal(final String principal, final String password) throws Exception {
kdc.createPrincipal(principal, password);
}
} }

View File

@ -37,7 +37,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public class KeytabUserIT { public class KerberosUserIT {
@ClassRule @ClassRule
public static TemporaryFolder tmpDir = new TemporaryFolder(); public static TemporaryFolder tmpDir = new TemporaryFolder();
@ -50,6 +50,9 @@ public class KeytabUserIT {
private static KerberosPrincipal principal2; private static KerberosPrincipal principal2;
private static File principal2KeytabFile; private static File principal2KeytabFile;
private static KerberosPrincipal principal3;
private static final String principal3Password = "changeme";
@BeforeClass @BeforeClass
public static void setupClass() throws Exception { public static void setupClass() throws Exception {
kdc = new KDCServer(tmpDir.newFolder("mini-kdc_")); kdc = new KDCServer(tmpDir.newFolder("mini-kdc_"));
@ -58,31 +61,34 @@ public class KeytabUserIT {
principal1 = new KerberosPrincipal("user1@" + kdc.getRealm()); principal1 = new KerberosPrincipal("user1@" + kdc.getRealm());
principal1KeytabFile = tmpDir.newFile("user1.keytab"); principal1KeytabFile = tmpDir.newFile("user1.keytab");
kdc.createKeytabFile(principal1KeytabFile, "user1"); kdc.createKeytabPrincipal(principal1KeytabFile, "user1");
principal2 = new KerberosPrincipal("user2@" + kdc.getRealm()); principal2 = new KerberosPrincipal("user2@" + kdc.getRealm());
principal2KeytabFile = tmpDir.newFile("user2.keytab"); principal2KeytabFile = tmpDir.newFile("user2.keytab");
kdc.createKeytabFile(principal2KeytabFile, "user2"); kdc.createKeytabPrincipal(principal2KeytabFile, "user2");
principal3 = new KerberosPrincipal("user3@" + kdc.getRealm());
kdc.createPasswordPrincipal("user3", principal3Password);
} }
@Test @Test
public void testSuccessfulLoginAndLogout() throws LoginException { public void testKeytabUserSuccessfulLoginAndLogout() throws LoginException {
// perform login for user1 // perform login for user1
final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath()); final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
user1.login(); user1.login();
// perform login for user2 // perform login for user2
final KeytabUser user2 = new StandardKeytabUser(principal2.getName(), principal2KeytabFile.getAbsolutePath()); final KerberosUser user2 = new KerberosKeytabUser(principal2.getName(), principal2KeytabFile.getAbsolutePath());
user2.login(); user2.login();
// verify user1 Subject only has user1 principal // verify user1 Subject only has user1 principal
final Subject user1Subject = ((StandardKeytabUser) user1).getSubject(); final Subject user1Subject = ((KerberosKeytabUser) user1).getSubject();
final Set<Principal> user1SubjectPrincipals = user1Subject.getPrincipals(); final Set<Principal> user1SubjectPrincipals = user1Subject.getPrincipals();
assertEquals(1, user1SubjectPrincipals.size()); assertEquals(1, user1SubjectPrincipals.size());
assertEquals(principal1.getName(), user1SubjectPrincipals.iterator().next().getName()); assertEquals(principal1.getName(), user1SubjectPrincipals.iterator().next().getName());
// verify user2 Subject only has user2 principal // verify user2 Subject only has user2 principal
final Subject user2Subject = ((StandardKeytabUser) user2).getSubject(); final Subject user2Subject = ((KerberosKeytabUser) user2).getSubject();
final Set<Principal> user2SubjectPrincipals = user2Subject.getPrincipals(); final Set<Principal> user2SubjectPrincipals = user2Subject.getPrincipals();
assertEquals(1, user2SubjectPrincipals.size()); assertEquals(1, user2SubjectPrincipals.size());
assertEquals(principal2.getName(), user2SubjectPrincipals.iterator().next().getName()); assertEquals(principal2.getName(), user2SubjectPrincipals.iterator().next().getName());
@ -101,9 +107,9 @@ public class KeytabUserIT {
} }
@Test @Test
public void testLoginWithUnknownPrincipal() throws LoginException { public void testKeytabLoginWithUnknownPrincipal() throws LoginException {
final String unknownPrincipal = "doesnotexist@" + kdc.getRealm(); final String unknownPrincipal = "doesnotexist@" + kdc.getRealm();
final KeytabUser user1 = new StandardKeytabUser(unknownPrincipal, principal1KeytabFile.getAbsolutePath()); final KerberosUser user1 = new KerberosKeytabUser(unknownPrincipal, principal1KeytabFile.getAbsolutePath());
try { try {
user1.login(); user1.login();
fail("Login should have failed"); fail("Login should have failed");
@ -113,9 +119,38 @@ public class KeytabUserIT {
} }
} }
@Test
public void testPasswordUserSuccessfulLoginAndLogout() throws LoginException {
// perform login for user
final KerberosUser user = new KerberosPasswordUser(principal3.getName(), principal3Password);
user.login();
// verify user Subject only has user principal
final Subject userSubject = ((KerberosPasswordUser) user).getSubject();
final Set<Principal> userSubjectPrincipals = userSubject.getPrincipals();
assertEquals(1, userSubjectPrincipals.size());
assertEquals(principal3.getName(), userSubjectPrincipals.iterator().next().getName());
// call check/relogin and verify neither user performed a relogin
assertFalse(user.checkTGTAndRelogin());
// perform logout for both users
user.logout();
// verify subjects have no more principals
assertEquals(0, userSubject.getPrincipals().size());
}
@Test(expected = LoginException.class)
public void testPasswordUserLoginWithInvalidPassword() throws LoginException {
// perform login for user
final KerberosUser user = new KerberosPasswordUser("user3", "NOT THE PASSWORD");
user.login();
}
@Test @Test
public void testCheckTGTAndRelogin() throws LoginException, InterruptedException { public void testCheckTGTAndRelogin() throws LoginException, InterruptedException {
final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath()); final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
user1.login(); user1.login();
// Since we set the lifetime to 15 seconds we should hit a relogin before 15 attempts // Since we set the lifetime to 15 seconds we should hit a relogin before 15 attempts
@ -136,7 +171,7 @@ public class KeytabUserIT {
@Test @Test
public void testKeytabAction() { public void testKeytabAction() {
final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath()); final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath());
final AtomicReference<String> resultHolder = new AtomicReference<>(null); final AtomicReference<String> resultHolder = new AtomicReference<>(null);
final PrivilegedAction privilegedAction = () -> { final PrivilegedAction privilegedAction = () -> {
@ -148,8 +183,8 @@ public class KeytabUserIT {
final ComponentLog logger = Mockito.mock(ComponentLog.class); final ComponentLog logger = Mockito.mock(ComponentLog.class);
// create the action to test and execute it // create the action to test and execute it
final KeytabAction keytabAction = new KeytabAction(user1, privilegedAction, context, logger); final KerberosAction kerberosAction = new KerberosAction(user1, privilegedAction, context, logger);
keytabAction.execute(); kerberosAction.execute();
// if the result holder has the string success then we know the action executed // if the result holder has the string success then we know the action executed
assertEquals("SUCCESS", resultHolder.get()); assertEquals("SUCCESS", resultHolder.get());

View File

@ -39,7 +39,7 @@ public class TestKeytabConfiguration {
assertEquals(1, entries.length); assertEquals(1, entries.length);
final AppConfigurationEntry entry = entries[0]; final AppConfigurationEntry entry = entries[0];
assertEquals(KeytabConfiguration.SUN_KRB5_LOGIN_MODULE, entry.getLoginModuleName()); assertEquals(ConfigurationUtil.SUN_KRB5_LOGIN_MODULE, entry.getLoginModuleName());
assertEquals(principal, entry.getOptions().get("principal")); assertEquals(principal, entry.getOptions().get("principal"));
assertEquals(keytab, entry.getOptions().get("keyTab")); assertEquals(keytab, entry.getOptions().get("keyTab"));
} }

View File

@ -28,9 +28,9 @@ import org.apache.nifi.processor.AbstractProcessor;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.ProcessSession; import org.apache.nifi.processor.ProcessSession;
import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.security.krb.KeytabAction; import org.apache.nifi.security.krb.KerberosAction;
import org.apache.nifi.security.krb.KeytabUser; import org.apache.nifi.security.krb.KerberosUser;
import org.apache.nifi.security.krb.StandardKeytabUser; import org.apache.nifi.security.krb.KerberosKeytabUser;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrClient;
@ -63,7 +63,7 @@ public abstract class SolrProcessor extends AbstractProcessor {
private volatile String basicPassword; private volatile String basicPassword;
private volatile boolean basicAuthEnabled = false; private volatile boolean basicAuthEnabled = false;
private volatile KeytabUser keytabUser; private volatile KerberosUser kerberosUser;
@OnScheduled @OnScheduled
public final void onScheduled(final ProcessContext context) throws IOException { public final void onScheduled(final ProcessContext context) throws IOException {
@ -78,12 +78,12 @@ public abstract class SolrProcessor extends AbstractProcessor {
final KerberosCredentialsService kerberosCredentialsService = context.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class); final KerberosCredentialsService kerberosCredentialsService = context.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class);
if (kerberosCredentialsService != null) { if (kerberosCredentialsService != null) {
this.keytabUser = createKeytabUser(kerberosCredentialsService); this.kerberosUser = createKeytabUser(kerberosCredentialsService);
} }
} }
protected KeytabUser createKeytabUser(final KerberosCredentialsService kerberosCredentialsService) { protected KerberosUser createKeytabUser(final KerberosCredentialsService kerberosCredentialsService) {
return new StandardKeytabUser(kerberosCredentialsService.getPrincipal(), kerberosCredentialsService.getKeytab()); return new KerberosKeytabUser(kerberosCredentialsService.getPrincipal(), kerberosCredentialsService.getKeytab());
} }
@OnStopped @OnStopped
@ -96,10 +96,10 @@ public abstract class SolrProcessor extends AbstractProcessor {
} }
} }
if (keytabUser != null) { if (kerberosUser != null) {
try { try {
keytabUser.logout(); kerberosUser.logout();
keytabUser = null; kerberosUser = null;
} catch (LoginException e) { } catch (LoginException e) {
getLogger().debug("Error logging out keytab user", e); getLogger().debug("Error logging out keytab user", e);
} }
@ -108,8 +108,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
@Override @Override
public final void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException { public final void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
final KeytabUser keytabUser = getKerberosKeytabUser(); final KerberosUser kerberosUser = getKerberosKeytabUser();
if (keytabUser == null) { if (kerberosUser == null) {
doOnTrigger(context, session); doOnTrigger(context, session);
} else { } else {
// wrap doOnTrigger in a privileged action // wrap doOnTrigger in a privileged action
@ -119,8 +119,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
}; };
// execute the privileged action as the given keytab user // execute the privileged action as the given keytab user
final KeytabAction keytabAction = new KeytabAction(keytabUser, action, context, getLogger()); final KerberosAction kerberosAction = new KerberosAction(kerberosUser, action, context, getLogger());
keytabAction.execute(); kerberosAction.execute();
} }
} }
@ -168,8 +168,8 @@ public abstract class SolrProcessor extends AbstractProcessor {
return basicAuthEnabled; return basicAuthEnabled;
} }
protected final KeytabUser getKerberosKeytabUser() { protected final KerberosUser getKerberosKeytabUser() {
return keytabUser; return kerberosUser;
} }
@Override @Override

View File

@ -21,7 +21,8 @@ import org.apache.nifi.kerberos.KerberosCredentialsService;
import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessContext;
import org.apache.nifi.processor.exception.ProcessException; import org.apache.nifi.processor.exception.ProcessException;
import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.reporting.InitializationException;
import org.apache.nifi.security.krb.KeytabUser; import org.apache.nifi.security.krb.KerberosKeytabUser;
import org.apache.nifi.security.krb.KerberosUser;
import org.apache.nifi.ssl.SSLContextService; import org.apache.nifi.ssl.SSLContextService;
import org.apache.nifi.util.TestRunner; import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners; import org.apache.nifi.util.TestRunners;
@ -451,10 +452,10 @@ public class TestPutSolrContentStream {
runner.assertValid(); runner.assertValid();
proc.onScheduled(runner.getProcessContext()); proc.onScheduled(runner.getProcessContext());
final KeytabUser keytabUser = proc.getMockKerberosKeytabUser(); final KerberosUser kerberosUser = proc.getMockKerberosKeytabUser();;
Assert.assertNotNull(keytabUser); Assert.assertNotNull(kerberosUser);
Assert.assertEquals(principal, keytabUser.getPrincipal()); Assert.assertEquals(principal, kerberosUser.getPrincipal());
Assert.assertEquals(keytab, keytabUser.getKeytabFile()); Assert.assertEquals(keytab, ((KerberosKeytabUser)kerberosUser).getKeytabFile());
} }
@Test @Test
@ -462,20 +463,20 @@ public class TestPutSolrContentStream {
final String principal = "nifi@FOO.COM"; final String principal = "nifi@FOO.COM";
final String keytab = "src/test/resources/foo.keytab"; final String keytab = "src/test/resources/foo.keytab";
// Setup a mock KeytabUser that will still execute the privileged action // Setup a mock KerberosUser that will still execute the privileged action
final KeytabUser keytabUser = Mockito.mock(KeytabUser.class); final KerberosKeytabUser kerberosUser = Mockito.mock(KerberosKeytabUser.class);
when(keytabUser.getPrincipal()).thenReturn(principal); when(kerberosUser.getPrincipal()).thenReturn(principal);
when(keytabUser.getKeytabFile()).thenReturn(keytab); when(kerberosUser.getKeytabFile()).thenReturn(keytab);
when(keytabUser.doAs(any(PrivilegedAction.class))).thenAnswer((invocation -> { when(kerberosUser.doAs(any(PrivilegedAction.class))).thenAnswer((invocation -> {
final PrivilegedAction action = (PrivilegedAction) invocation.getArguments()[0]; final PrivilegedAction action = (PrivilegedAction) invocation.getArguments()[0];
action.run(); action.run();
return null; return null;
}) })
); );
// Configure the processor with the mock KeytabUser and with a credentials service // Configure the processor with the mock KerberosUser and with a credentials service
final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE); final SolrClient solrClient = createEmbeddedSolrClient(DEFAULT_SOLR_CORE);
final TestableProcessor proc = new TestableProcessor(solrClient, keytabUser); final TestableProcessor proc = new TestableProcessor(solrClient, kerberosUser);
final TestRunner runner = createDefaultTestRunner(proc); final TestRunner runner = createDefaultTestRunner(proc);
final KerberosCredentialsService kerberosCredentialsService = new MockKerberosCredentialsService(principal, keytab); final KerberosCredentialsService kerberosCredentialsService = new MockKerberosCredentialsService(principal, keytab);
@ -499,9 +500,9 @@ public class TestPutSolrContentStream {
} }
// Verify that during the update the user was logged in, TGT was checked, and the action was executed // Verify that during the update the user was logged in, TGT was checked, and the action was executed
verify(keytabUser, times(1)).login(); verify(kerberosUser, times(1)).login();
verify(keytabUser, times(1)).checkTGTAndRelogin(); verify(kerberosUser, times(1)).checkTGTAndRelogin();
verify(keytabUser, times(1)).doAs(any(PrivilegedAction.class)); verify(kerberosUser, times(1)).doAs(any(PrivilegedAction.class));
} }
@ -647,15 +648,15 @@ public class TestPutSolrContentStream {
// Override createSolrClient and return the passed in SolrClient // Override createSolrClient and return the passed in SolrClient
private class TestableProcessor extends PutSolrContentStream { private class TestableProcessor extends PutSolrContentStream {
private SolrClient solrClient; private SolrClient solrClient;
private KeytabUser keytabUser; private KerberosUser kerberosUser;
public TestableProcessor(SolrClient solrClient) { public TestableProcessor(SolrClient solrClient) {
this.solrClient = solrClient; this.solrClient = solrClient;
} }
public TestableProcessor(SolrClient solrClient, KeytabUser keytabUser) { public TestableProcessor(SolrClient solrClient, KerberosUser kerberosUser) {
this.solrClient = solrClient; this.solrClient = solrClient;
this.keytabUser = keytabUser; this.kerberosUser = kerberosUser;
} }
@Override @Override
@ -664,15 +665,15 @@ public class TestPutSolrContentStream {
} }
@Override @Override
protected KeytabUser createKeytabUser(KerberosCredentialsService kerberosCredentialsService) { protected KerberosUser createKeytabUser(KerberosCredentialsService kerberosCredentialsService) {
if (keytabUser != null) { if (kerberosUser != null) {
return keytabUser; return kerberosUser;
} else { } else {
return super.createKeytabUser(kerberosCredentialsService); return super.createKeytabUser(kerberosCredentialsService);
} }
} }
public KeytabUser getMockKerberosKeytabUser() { public KerberosUser getMockKerberosKeytabUser() {
return super.getKerberosKeytabUser(); return super.getKerberosKeytabUser();
} }
} }