This is an automated email from the ASF dual-hosted git repository. mattyb149 pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/master by this push: new 2bbfb32 NIFI-5945 Add support for password login to kerberos code in nifi-security-utils 2bbfb32 is described below commit 2bbfb3217be40abe4af7ddb8627808d12d99bb17 Author: Bryan Bende <bbe...@apache.org> AuthorDate: Wed Jan 9 17:37:10 2019 -0500 NIFI-5945 Add support for password login to kerberos code in nifi-security-utils Fixing solr test Signed-off-by: Matthew Burgess <mattyb...@apache.org> This closes #3256 --- ...rdKeytabUser.java => AbstractKerberosUser.java} | 41 +++----- .../nifi/security/krb/ConfigurationUtil.java | 25 +++++ .../krb/{KeytabAction.java => KerberosAction.java} | 34 +++---- .../nifi/security/krb/KerberosKeytabUser.java | 59 +++++++++++ .../nifi/security/krb/KerberosPasswordUser.java | 110 +++++++++++++++++++++ .../krb/{KeytabUser.java => KerberosUser.java} | 7 +- .../nifi/security/krb/KeytabConfiguration.java | 9 +- .../org/apache/nifi/security/krb/KDCServer.java | 5 +- .../krb/{KeytabUserIT.java => KerberosUserIT.java} | 63 +++++++++--- .../nifi/security/krb/TestKeytabConfiguration.java | 2 +- .../apache/nifi/processors/solr/SolrProcessor.java | 32 +++--- .../processors/solr/TestPutSolrContentStream.java | 45 ++++----- 12 files changed, 320 insertions(+), 112 deletions(-) diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/StandardKeytabUser.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java similarity index 86% rename from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/StandardKeytabUser.java rename to nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java index 7302ee0..32eb9bb 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/StandardKeytabUser.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java @@ -23,7 +23,6 @@ import org.slf4j.LoggerFactory; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.kerberos.KerberosTicket; -import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import java.security.PrivilegedAction; @@ -34,14 +33,9 @@ import java.util.Date; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -/** - * 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 { +public abstract class AbstractKerberosUser implements KerberosUser { - 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'"; @@ -50,18 +44,15 @@ public class StandardKeytabUser implements KeytabUser { */ static final float TICKET_RENEW_WINDOW = 0.80f; - private final String principal; - private final String keytabFile; - private final AtomicBoolean loggedIn = new AtomicBoolean(false); + protected final String principal; + protected final AtomicBoolean loggedIn = new AtomicBoolean(false); - private Subject subject; - private LoginContext loginContext; + protected Subject subject; + protected LoginContext loginContext; - public StandardKeytabUser(final String principal, final String keytabFile) { + public AbstractKerberosUser(final String principal) { this.principal = principal; - this.keytabFile = keytabFile; - Validate.notBlank(principal); - Validate.notBlank(keytabFile); + Validate.notBlank(this.principal); } /** @@ -80,19 +71,19 @@ public class StandardKeytabUser implements KeytabUser { if (loginContext == null) { LOGGER.debug("Initializing new login context..."); this.subject = new Subject(); - - final Configuration config = new KeytabConfiguration(principal, keytabFile); - this.loginContext = new LoginContext("KeytabConf", subject, null, config); + this.loginContext = createLoginContext(subject); } loginContext.login(); loggedIn.set(true); LOGGER.debug("Successful login for {}", new Object[]{principal}); } 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. * @@ -244,14 +235,6 @@ public class StandardKeytabUser implements KeytabUser { return principal; } - /** - * @return the keytab file for this user - */ - @Override - public String getKeytabFile() { - return keytabFile; - } - // Visible for testing Subject getSubject() { return this.subject; diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/ConfigurationUtil.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/ConfigurationUtil.java new file mode 100644 index 0000000..131ff22 --- /dev/null +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/ConfigurationUtil.java @@ -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"; + +} diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabAction.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosAction.java similarity index 79% rename from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabAction.java rename to nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosAction.java index 5e3f592..bd3e1f9 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabAction.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosAction.java @@ -25,24 +25,24 @@ import javax.security.auth.login.LoginException; 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 ProcessContext context; private final ComponentLog logger; - public KeytabAction(final KeytabUser keytabUser, - final PrivilegedAction action, - final ProcessContext context, - final ComponentLog logger) { - this.keytabUser = keytabUser; + public KerberosAction(final KerberosUser kerberosUser, + final PrivilegedAction action, + final ProcessContext context, + final ComponentLog logger) { + this.kerberosUser = kerberosUser; this.action = action; this.context = context; this.logger = logger; - Validate.notNull(this.keytabUser); + Validate.notNull(this.kerberosUser); Validate.notNull(this.action); Validate.notNull(this.context); Validate.notNull(this.logger); @@ -50,10 +50,10 @@ public class KeytabAction { public void execute() { // lazily login the first time the processor executes - if (!keytabUser.isLoggedIn()) { + if (!kerberosUser.isLoggedIn()) { try { - keytabUser.login(); - logger.info("Successful login for {}", new Object[]{keytabUser.getPrincipal()}); + kerberosUser.login(); + logger.info("Successful login for {}", new Object[]{kerberosUser.getPrincipal()}); } catch (LoginException e) { // make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately 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) try { - keytabUser.checkTGTAndRelogin(); + kerberosUser.checkTGTAndRelogin(); } catch (LoginException e) { // make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately 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 try { - keytabUser.doAs(action); + kerberosUser.doAs(action); } catch (SecurityException se) { logger.info("Privileged action failed, attempting relogin and retrying..."); logger.debug("", se); try { - keytabUser.logout(); - keytabUser.login(); - keytabUser.doAs(action); + kerberosUser.logout(); + kerberosUser.login(); + kerberosUser.doAs(action); } catch (Exception e) { // make sure to yield so the processor doesn't keep retrying the rolled back flow files immediately context.yield(); diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosKeytabUser.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosKeytabUser.java new file mode 100644 index 0000000..a2af82e --- /dev/null +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosKeytabUser.java @@ -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; + } + +} diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosPasswordUser.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosPasswordUser.java new file mode 100644 index 0000000..d81fc85 --- /dev/null +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosPasswordUser.java @@ -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()); + } + } + } + + } + +} diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabUser.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosUser.java similarity index 95% rename from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabUser.java rename to nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosUser.java index 42089c2..16e4fd2 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabUser.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosUser.java @@ -24,7 +24,7 @@ import java.security.PrivilegedExceptionAction; /** * 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. @@ -80,9 +80,4 @@ public interface KeytabUser { */ String getPrincipal(); - /** - * @return the keytab file for this user - */ - String getKeytabFile(); - } diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabConfiguration.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabConfiguration.java index 0ad0efe..24af9b2 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabConfiguration.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabConfiguration.java @@ -28,10 +28,6 @@ import java.util.Map; */ 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 keytabFile; @@ -53,7 +49,7 @@ public class KeytabConfiguration extends Configuration { options.put("principal", principal); options.put("refreshKrb5Config", "true"); - if (IS_IBM) { + if (ConfigurationUtil.IS_IBM) { options.put("useKeytab", keytabFile); options.put("credsType", "both"); } else { @@ -64,7 +60,8 @@ public class KeytabConfiguration extends Configuration { 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( krbLoginModuleName, AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KDCServer.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KDCServer.java index 5669b07..478b4de 100644 --- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KDCServer.java +++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KDCServer.java @@ -67,8 +67,11 @@ public class KDCServer { 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); } + public void createPasswordPrincipal(final String principal, final String password) throws Exception { + kdc.createPrincipal(principal, password); + } } diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KeytabUserIT.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java similarity index 65% rename from nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KeytabUserIT.java rename to nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java index 795f4fb..e636008 100644 --- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KeytabUserIT.java +++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java @@ -37,7 +37,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; -public class KeytabUserIT { +public class KerberosUserIT { @ClassRule public static TemporaryFolder tmpDir = new TemporaryFolder(); @@ -50,6 +50,9 @@ public class KeytabUserIT { private static KerberosPrincipal principal2; private static File principal2KeytabFile; + private static KerberosPrincipal principal3; + private static final String principal3Password = "changeme"; + @BeforeClass public static void setupClass() throws Exception { kdc = new KDCServer(tmpDir.newFolder("mini-kdc_")); @@ -58,31 +61,34 @@ public class KeytabUserIT { principal1 = new KerberosPrincipal("user1@" + kdc.getRealm()); principal1KeytabFile = tmpDir.newFile("user1.keytab"); - kdc.createKeytabFile(principal1KeytabFile, "user1"); + kdc.createKeytabPrincipal(principal1KeytabFile, "user1"); principal2 = new KerberosPrincipal("user2@" + kdc.getRealm()); principal2KeytabFile = tmpDir.newFile("user2.keytab"); - kdc.createKeytabFile(principal2KeytabFile, "user2"); + kdc.createKeytabPrincipal(principal2KeytabFile, "user2"); + + principal3 = new KerberosPrincipal("user3@" + kdc.getRealm()); + kdc.createPasswordPrincipal("user3", principal3Password); } @Test - public void testSuccessfulLoginAndLogout() throws LoginException { + public void testKeytabUserSuccessfulLoginAndLogout() throws LoginException { // perform login for user1 - final KeytabUser user1 = new StandardKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath()); + final KerberosUser user1 = new KerberosKeytabUser(principal1.getName(), principal1KeytabFile.getAbsolutePath()); user1.login(); // perform login for user2 - final KeytabUser user2 = new StandardKeytabUser(principal2.getName(), principal2KeytabFile.getAbsolutePath()); + final KerberosUser user2 = new KerberosKeytabUser(principal2.getName(), principal2KeytabFile.getAbsolutePath()); user2.login(); // 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(); assertEquals(1, user1SubjectPrincipals.size()); assertEquals(principal1.getName(), user1SubjectPrincipals.iterator().next().getName()); // 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(); assertEquals(1, user2SubjectPrincipals.size()); assertEquals(principal2.getName(), user2SubjectPrincipals.iterator().next().getName()); @@ -101,9 +107,9 @@ public class KeytabUserIT { } @Test - public void testLoginWithUnknownPrincipal() throws LoginException { + public void testKeytabLoginWithUnknownPrincipal() throws LoginException { final String unknownPrincipal = "doesnotexist@" + kdc.getRealm(); - final KeytabUser user1 = new StandardKeytabUser(unknownPrincipal, principal1KeytabFile.getAbsolutePath()); + final KerberosUser user1 = new KerberosKeytabUser(unknownPrincipal, principal1KeytabFile.getAbsolutePath()); try { user1.login(); fail("Login should have failed"); @@ -114,8 +120,37 @@ 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 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(); // Since we set the lifetime to 15 seconds we should hit a relogin before 15 attempts @@ -136,7 +171,7 @@ public class KeytabUserIT { @Test 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 PrivilegedAction privilegedAction = () -> { @@ -148,8 +183,8 @@ public class KeytabUserIT { final ComponentLog logger = Mockito.mock(ComponentLog.class); // create the action to test and execute it - final KeytabAction keytabAction = new KeytabAction(user1, privilegedAction, context, logger); - keytabAction.execute(); + final KerberosAction kerberosAction = new KerberosAction(user1, privilegedAction, context, logger); + kerberosAction.execute(); // if the result holder has the string success then we know the action executed assertEquals("SUCCESS", resultHolder.get()); diff --git a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/TestKeytabConfiguration.java b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/TestKeytabConfiguration.java index f105636..663fa06 100644 --- a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/TestKeytabConfiguration.java +++ b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/TestKeytabConfiguration.java @@ -39,7 +39,7 @@ public class TestKeytabConfiguration { assertEquals(1, entries.length); 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(keytab, entry.getOptions().get("keyTab")); } diff --git a/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/main/java/org/apache/nifi/processors/solr/SolrProcessor.java b/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/main/java/org/apache/nifi/processors/solr/SolrProcessor.java index fa44b2e..4a193b6 100755 --- a/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/main/java/org/apache/nifi/processors/solr/SolrProcessor.java +++ b/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/main/java/org/apache/nifi/processors/solr/SolrProcessor.java @@ -28,9 +28,9 @@ import org.apache.nifi.processor.AbstractProcessor; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.ProcessSession; import org.apache.nifi.processor.exception.ProcessException; -import org.apache.nifi.security.krb.KeytabAction; -import org.apache.nifi.security.krb.KeytabUser; -import org.apache.nifi.security.krb.StandardKeytabUser; +import org.apache.nifi.security.krb.KerberosAction; +import org.apache.nifi.security.krb.KerberosUser; +import org.apache.nifi.security.krb.KerberosKeytabUser; import org.apache.nifi.ssl.SSLContextService; import org.apache.solr.client.solrj.SolrClient; @@ -63,7 +63,7 @@ public abstract class SolrProcessor extends AbstractProcessor { private volatile String basicPassword; private volatile boolean basicAuthEnabled = false; - private volatile KeytabUser keytabUser; + private volatile KerberosUser kerberosUser; @OnScheduled 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); if (kerberosCredentialsService != null) { - this.keytabUser = createKeytabUser(kerberosCredentialsService); + this.kerberosUser = createKeytabUser(kerberosCredentialsService); } } - protected KeytabUser createKeytabUser(final KerberosCredentialsService kerberosCredentialsService) { - return new StandardKeytabUser(kerberosCredentialsService.getPrincipal(), kerberosCredentialsService.getKeytab()); + protected KerberosUser createKeytabUser(final KerberosCredentialsService kerberosCredentialsService) { + return new KerberosKeytabUser(kerberosCredentialsService.getPrincipal(), kerberosCredentialsService.getKeytab()); } @OnStopped @@ -96,10 +96,10 @@ public abstract class SolrProcessor extends AbstractProcessor { } } - if (keytabUser != null) { + if (kerberosUser != null) { try { - keytabUser.logout(); - keytabUser = null; + kerberosUser.logout(); + kerberosUser = null; } catch (LoginException e) { getLogger().debug("Error logging out keytab user", e); } @@ -108,8 +108,8 @@ public abstract class SolrProcessor extends AbstractProcessor { @Override public final void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException { - final KeytabUser keytabUser = getKerberosKeytabUser(); - if (keytabUser == null) { + final KerberosUser kerberosUser = getKerberosKeytabUser(); + if (kerberosUser == null) { doOnTrigger(context, session); } else { // 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 - final KeytabAction keytabAction = new KeytabAction(keytabUser, action, context, getLogger()); - keytabAction.execute(); + final KerberosAction kerberosAction = new KerberosAction(kerberosUser, action, context, getLogger()); + kerberosAction.execute(); } } @@ -168,8 +168,8 @@ public abstract class SolrProcessor extends AbstractProcessor { return basicAuthEnabled; } - protected final KeytabUser getKerberosKeytabUser() { - return keytabUser; + protected final KerberosUser getKerberosKeytabUser() { + return kerberosUser; } @Override diff --git a/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/test/java/org/apache/nifi/processors/solr/TestPutSolrContentStream.java b/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/test/java/org/apache/nifi/processors/solr/TestPutSolrContentStream.java index 4eeb12a..e3c3b79 100755 --- a/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/test/java/org/apache/nifi/processors/solr/TestPutSolrContentStream.java +++ b/nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/test/java/org/apache/nifi/processors/solr/TestPutSolrContentStream.java @@ -21,7 +21,8 @@ import org.apache.nifi.kerberos.KerberosCredentialsService; import org.apache.nifi.processor.ProcessContext; import org.apache.nifi.processor.exception.ProcessException; 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.util.TestRunner; import org.apache.nifi.util.TestRunners; @@ -451,10 +452,10 @@ public class TestPutSolrContentStream { runner.assertValid(); proc.onScheduled(runner.getProcessContext()); - final KeytabUser keytabUser = proc.getMockKerberosKeytabUser(); - Assert.assertNotNull(keytabUser); - Assert.assertEquals(principal, keytabUser.getPrincipal()); - Assert.assertEquals(keytab, keytabUser.getKeytabFile()); + final KerberosUser kerberosUser = proc.getMockKerberosKeytabUser();; + Assert.assertNotNull(kerberosUser); + Assert.assertEquals(principal, kerberosUser.getPrincipal()); + Assert.assertEquals(keytab, ((KerberosKeytabUser)kerberosUser).getKeytabFile()); } @Test @@ -462,20 +463,20 @@ public class TestPutSolrContentStream { final String principal = "n...@foo.com"; final String keytab = "src/test/resources/foo.keytab"; - // Setup a mock KeytabUser that will still execute the privileged action - final KeytabUser keytabUser = Mockito.mock(KeytabUser.class); - when(keytabUser.getPrincipal()).thenReturn(principal); - when(keytabUser.getKeytabFile()).thenReturn(keytab); - when(keytabUser.doAs(any(PrivilegedAction.class))).thenAnswer((invocation -> { + // Setup a mock KerberosUser that will still execute the privileged action + final KerberosKeytabUser kerberosUser = Mockito.mock(KerberosKeytabUser.class); + when(kerberosUser.getPrincipal()).thenReturn(principal); + when(kerberosUser.getKeytabFile()).thenReturn(keytab); + when(kerberosUser.doAs(any(PrivilegedAction.class))).thenAnswer((invocation -> { final PrivilegedAction action = (PrivilegedAction) invocation.getArguments()[0]; action.run(); 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 TestableProcessor proc = new TestableProcessor(solrClient, keytabUser); + final TestableProcessor proc = new TestableProcessor(solrClient, kerberosUser); final TestRunner runner = createDefaultTestRunner(proc); 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(keytabUser, times(1)).login(); - verify(keytabUser, times(1)).checkTGTAndRelogin(); - verify(keytabUser, times(1)).doAs(any(PrivilegedAction.class)); + verify(kerberosUser, times(1)).login(); + verify(kerberosUser, times(1)).checkTGTAndRelogin(); + verify(kerberosUser, times(1)).doAs(any(PrivilegedAction.class)); } @@ -647,15 +648,15 @@ public class TestPutSolrContentStream { // Override createSolrClient and return the passed in SolrClient private class TestableProcessor extends PutSolrContentStream { private SolrClient solrClient; - private KeytabUser keytabUser; + private KerberosUser kerberosUser; public TestableProcessor(SolrClient solrClient) { this.solrClient = solrClient; } - public TestableProcessor(SolrClient solrClient, KeytabUser keytabUser) { + public TestableProcessor(SolrClient solrClient, KerberosUser kerberosUser) { this.solrClient = solrClient; - this.keytabUser = keytabUser; + this.kerberosUser = kerberosUser; } @Override @@ -664,15 +665,15 @@ public class TestPutSolrContentStream { } @Override - protected KeytabUser createKeytabUser(KerberosCredentialsService kerberosCredentialsService) { - if (keytabUser != null) { - return keytabUser; + protected KerberosUser createKeytabUser(KerberosCredentialsService kerberosCredentialsService) { + if (kerberosUser != null) { + return kerberosUser; } else { return super.createKeytabUser(kerberosCredentialsService); } } - public KeytabUser getMockKerberosKeytabUser() { + public KerberosUser getMockKerberosKeytabUser() { return super.getKerberosKeytabUser(); } }