NIFI-5945 Add support for password login to kerberos code in nifi-security-utils
authorBryan Bende <bbende@apache.org>
Wed, 9 Jan 2019 22:37:10 +0000 (17:37 -0500)
committerMatthew Burgess <mattyb149@apache.org>
Thu, 10 Jan 2019 20:05:31 +0000 (15:05 -0500)
Fixing solr test

Signed-off-by: Matthew Burgess <mattyb149@apache.org>
This closes #3256

12 files changed:
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java [moved from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/StandardKeytabUser.java with 86% similarity]
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/ConfigurationUtil.java [new file with mode: 0644]
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosAction.java [moved from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabAction.java with 79% similarity]
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosKeytabUser.java [new file with mode: 0644]
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosPasswordUser.java [new file with mode: 0644]
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KerberosUser.java [moved from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabUser.java with 95% similarity]
nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/KeytabConfiguration.java
nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KDCServer.java
nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java [moved from nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KeytabUserIT.java with 65% similarity]
nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/TestKeytabConfiguration.java
nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/main/java/org/apache/nifi/processors/solr/SolrProcessor.java
nifi-nar-bundles/nifi-solr-bundle/nifi-solr-processors/src/test/java/org/apache/nifi/processors/solr/TestPutSolrContentStream.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 (file)
index 0000000..131ff22
--- /dev/null
@@ -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";
+
+}
@@ -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 (file)
index 0000000..a2af82e
--- /dev/null
@@ -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 (file)
index 0000000..d81fc85
--- /dev/null
@@ -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());
+                }
+            }
+        }
+
+    }
+
+}
@@ -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();
-
 }
index 0ad0efe..24af9b2 100644 (file)
@@ -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);
index 5669b07..478b4de 100644 (file)
@@ -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);
+    }
 }
@@ -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());
index f105636..663fa06 100644 (file)
@@ -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"));
     }
index fa44b2e..4a193b6 100755 (executable)
@@ -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
index 4eeb12a..e3c3b79 100755 (executable)
@@ -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 = "nifi@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();
         }
     }