This is an automated email from the ASF dual-hosted git repository.

bbende 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 614136c  NIFI-7018: Initial commit of processors extending 
AbstractHadoopProcessor supporting kerberos passwords AbstractHadoopProcessor 
will always authenticate the principal with a KerberosUser implementation and a 
UGI will be acquired from the Subject associated with the KerberosUser 
implementation AbstractHadoopProcessor's getUserGroupInformation method will 
now attempt to check the TGT and relogin if a KerberosUser impelmentation is 
available, otherwise it will return the UG [...]
614136c is described below

commit 614136ce51638fc8ca28cab47d4e5bfa253b6cc4
Author: jstorck <[email protected]>
AuthorDate: Tue Feb 25 23:31:06 2020 -0500

    NIFI-7018: Initial commit of processors extending AbstractHadoopProcessor 
supporting kerberos passwords
    AbstractHadoopProcessor will always authenticate the principal with a 
KerberosUser implementation and a UGI will be acquired from the Subject 
associated with the KerberosUser implementation
    AbstractHadoopProcessor's getUserGroupInformation method will now attempt 
to check the TGT and relogin if a KerberosUser impelmentation is available, 
otherwise it will return the UGI referenced in the HdfsResource instance
    Updated AbstractHadoopProcessor's customValidate method to consider the 
provided password and updated validation failure explanations when a 
KerberosCredentialsService is specified together with a principal, password, or 
keytab
    Added toString method override to AbstractKerberosUser
    Updated Hive/HBase components to be compatible with the 
KerberosProperties.validatePrincipalWithKeytabOrPassword method
    Fixed null ComponentLog in GetHDFSSequenceFileTest
    
    Added package-protected accessor method 
(getAllowExplicitKeytabEnvironmentVariable) to AbstractHadoopProcessor for 
determining if the environment variable "NIFI_ALLOW_EXPLICIT_KEYTAB" has been 
set
    AbstractHadoopProcessor will now only fail validation when the 
NIFI_ALLOW_EXPLICIT_KEYTAB environment variable is set to false if a keytab is 
provided to allow the user to specify a principal and password
    Added AbstractHadoopProcessorSpec to verify validation of 
principal/keytab/password/kerberos credential service combinations
    
    This closes #4095.
---
 .../nifi/security/krb/AbstractKerberosUser.java    |  14 ++-
 .../apache/nifi/security/krb/KerberosUserIT.java   |  24 +++++
 .../nifi-extension-utils/nifi-hadoop-utils/pom.xml |  21 ++++
 .../org/apache/nifi/hadoop/KerberosProperties.java |  34 ++++++-
 .../java/org/apache/nifi/hadoop/SecurityUtil.java  |  32 ++++++
 .../processors/hadoop/AbstractHadoopProcessor.java |  86 ++++++++++++----
 .../apache/nifi/hadoop/SecurityUtilITSpec.groovy   | 101 ++++++++++++++++++
 .../hadoop/AbstractHadoopProcessorSpec.groovy      | 113 +++++++++++++++++++++
 .../apache/nifi/hadoop/TestKerberosProperties.java |  62 +++++++++--
 .../src/test/resources/log4j.properties            |  30 ++++++
 .../src/test/resources/test-secure-core-site.xml   |  21 ++++
 .../processors/hadoop/GetHDFSSequenceFileTest.java |  11 +-
 .../apache/nifi/util/hive/HiveConfigurator.java    |   2 +-
 .../apache/nifi/util/hive/HiveConfigurator.java    |   2 +-
 .../apache/nifi/util/hive/HiveConfigurator.java    |   2 +-
 .../nifi/hbase/HBase_1_1_2_ClientService.java      |   2 +-
 .../apache/nifi/hbase/HBase_2_ClientService.java   |   2 +-
 17 files changed, 519 insertions(+), 40 deletions(-)

diff --git 
a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java
 
b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java
index 32eb9bb..6764497 100644
--- 
a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java
+++ 
b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/krb/AbstractKerberosUser.java
@@ -70,7 +70,11 @@ public abstract class AbstractKerberosUser implements 
KerberosUser {
             // If it's the first time ever calling login then we need to 
initialize a new context
             if (loginContext == null) {
                 LOGGER.debug("Initializing new login context...");
-                this.subject = new Subject();
+                if (this.subject == null) {
+                    // only create a new subject if a current one does not 
exist
+                    // other classes may be referencing an existing subject 
and replacing it may break functionality of those other classes after relogin
+                    this.subject = new Subject();
+                }
                 this.loginContext = createLoginContext(subject);
             }
 
@@ -100,7 +104,6 @@ public abstract class AbstractKerberosUser implements 
KerberosUser {
             loggedIn.set(false);
             LOGGER.debug("Successful logout for {}", new Object[]{principal});
 
-            subject = null;
             loginContext = null;
         } catch (LoginException e) {
             throw new LoginException("Logout failed due to: " + 
e.getMessage());
@@ -240,4 +243,11 @@ public abstract class AbstractKerberosUser implements 
KerberosUser {
         return this.subject;
     }
 
+    @Override
+    public String toString() {
+        return "KerberosUser{" +
+                "principal='" + principal + '\'' +
+                ", loggedIn=" + loggedIn +
+                '}';
+    }
 }
diff --git 
a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java
 
b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java
index 5a390d6..5f8c457 100644
--- 
a/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java
+++ 
b/nifi-commons/nifi-security-utils/src/test/java/org/apache/nifi/security/krb/KerberosUserIT.java
@@ -26,15 +26,21 @@ import org.mockito.Mockito;
 
 import javax.security.auth.Subject;
 import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.kerberos.KerberosTicket;
 import javax.security.auth.login.LoginException;
 import java.io.File;
+import java.security.AccessControlContext;
+import java.security.AccessController;
 import java.security.Principal;
+import java.security.PrivilegedAction;
 import java.security.PrivilegedExceptionAction;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 public class KerberosUserIT {
@@ -167,6 +173,24 @@ public class KerberosUserIT {
             }
         }
         assertEquals(true, performedRelogin);
+
+        Subject subject = user1.doAs((PrivilegedAction<Subject>) () -> {
+            AccessControlContext context = AccessController.getContext();
+            return Subject.getSubject(context);
+        });
+
+        // verify only a single KerberosTicket exists in the Subject after 
relogin
+        Set<KerberosTicket> kerberosTickets = 
subject.getPrivateCredentials(KerberosTicket.class);
+        assertEquals(1, kerberosTickets.size());
+
+        // verify the new ticket lifetime is valid for the current time
+        KerberosTicket kerberosTicket = kerberosTickets.iterator().next();
+        long currentTimeMillis = System.currentTimeMillis();
+        long startMilli = 
kerberosTicket.getStartTime().toInstant().toEpochMilli();
+        long endMilli = kerberosTicket.getEndTime().toInstant().toEpochMilli();
+        System.out.println("New ticket is valid for " + 
TimeUnit.MILLISECONDS.toSeconds(endMilli - startMilli) + " seconds");
+        assertTrue(startMilli < currentTimeMillis);
+        assertTrue(endMilli > currentTimeMillis);
     }
 
     @Test
diff --git a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/pom.xml 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/pom.xml
index 380a0a4..e3b69ae 100644
--- a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/pom.xml
+++ b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/pom.xml
@@ -36,6 +36,11 @@
         </dependency>
         <dependency>
             <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-security-utils</artifactId>
+            <version>1.12.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
             <artifactId>nifi-kerberos-credentials-service-api</artifactId>
         </dependency>
         <dependency>
@@ -62,6 +67,22 @@
             <version>4.5.5</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.spockframework</groupId>
+            <artifactId>spock-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-minikdc</artifactId>
+            <version>3.2.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/KerberosProperties.java
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/KerberosProperties.java
index 7d0210f..ba5e9ec 100644
--- 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/KerberosProperties.java
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/KerberosProperties.java
@@ -45,6 +45,7 @@ public class KerberosProperties {
     private final Validator kerberosConfigValidator;
     private final PropertyDescriptor kerberosPrincipal;
     private final PropertyDescriptor kerberosKeytab;
+    private final PropertyDescriptor kerberosPassword;
 
     /**
      * Instantiate a KerberosProperties object but keep in mind it is
@@ -90,13 +91,23 @@ public class KerberosProperties {
                 .build();
 
         this.kerberosKeytab = new PropertyDescriptor.Builder()
-                .name("Kerberos Keytab").required(false)
+                .name("Kerberos Keytab")
+                .required(false)
                 .description("Kerberos keytab associated with the principal. 
Requires nifi.kerberos.krb5.file to be set in your nifi.properties")
                 .addValidator(StandardValidators.FILE_EXISTS_VALIDATOR)
                 .addValidator(kerberosConfigValidator)
                 
.addValidator(StandardValidators.ATTRIBUTE_EXPRESSION_LANGUAGE_VALIDATOR)
                 
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
                 .build();
+
+        this.kerberosPassword = new PropertyDescriptor.Builder()
+                .name("Kerberos Password")
+                .required(false)
+                .description("Kerberos password associated with the 
principal.")
+                
.addValidator(StandardValidators.ATTRIBUTE_EXPRESSION_LANGUAGE_VALIDATOR)
+                
.expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY)
+                .sensitive(true)
+                .build();
     }
 
     public File getKerberosConfigFile() {
@@ -115,7 +126,12 @@ public class KerberosProperties {
         return kerberosKeytab;
     }
 
-    public static List<ValidationResult> validatePrincipalAndKeytab(final 
String subject, final Configuration config, final String principal, final 
String keytab, final ComponentLog logger) {
+    public PropertyDescriptor getKerberosPassword() {
+        return kerberosPassword;
+    }
+
+    public static List<ValidationResult> 
validatePrincipalWithKeytabOrPassword(final String subject, final Configuration 
config, final String principal, final String keytab,
+                                                                               
final String password, final ComponentLog logger) {
         final List<ValidationResult> results = new ArrayList<>();
 
         // if security is enabled then the keytab and principal are required
@@ -131,11 +147,21 @@ public class KerberosProperties {
         }
 
         final boolean blankKeytab = (keytab == null || keytab.isEmpty());
-        if (isSecurityEnabled && blankKeytab) {
+        final boolean blankPassword = (password == null || password.isEmpty());
+
+        if (isSecurityEnabled && blankKeytab && blankPassword) {
+            results.add(new ValidationResult.Builder()
+                    .valid(false)
+                    .subject(subject)
+                    .explanation("Kerberos Keytab or Kerberos Password must be 
provided when using a secure configuration")
+                    .build());
+        }
+
+        if (isSecurityEnabled && !blankKeytab && !blankPassword) {
             results.add(new ValidationResult.Builder()
                     .valid(false)
                     .subject(subject)
-                    .explanation("Kerberos Keytab must be provided when using 
a secure configuration")
+                    .explanation("Cannot specify both a Kerberos Keytab and a 
Kerberos Password")
                     .build());
         }
 
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/SecurityUtil.java
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/SecurityUtil.java
index af22757..6a8f079 100644
--- 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/SecurityUtil.java
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/hadoop/SecurityUtil.java
@@ -19,9 +19,18 @@ package org.apache.nifi.hadoop;
 import org.apache.commons.lang3.Validate;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.security.UserGroupInformation;
+import org.apache.nifi.security.krb.KerberosUser;
 
+import javax.security.auth.Subject;
+import javax.security.auth.kerberos.KerberosPrincipal;
+import javax.security.auth.login.LoginException;
 import java.io.IOException;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
 import java.util.Random;
+import java.util.stream.Collectors;
 
 /**
  * Provides synchronized access to UserGroupInformation to avoid multiple 
processors/services from
@@ -69,6 +78,29 @@ public class SecurityUtil {
         return UserGroupInformation.getCurrentUser();
     }
 
+    public static synchronized UserGroupInformation 
getUgiForKerberosUser(final Configuration config, final KerberosUser 
kerberosUser) throws IOException {
+        UserGroupInformation.setConfiguration(config);
+        try {
+            if (kerberosUser.isLoggedIn()) {
+                kerberosUser.checkTGTAndRelogin();
+            } else {
+                kerberosUser.login();
+            }
+            return 
kerberosUser.doAs((PrivilegedExceptionAction<UserGroupInformation>) () -> {
+                AccessControlContext context = AccessController.getContext();
+                Subject subject = Subject.getSubject(context);
+                Validate.notEmpty(
+                        
subject.getPrincipals(KerberosPrincipal.class).stream().filter(p -> 
p.getName().startsWith(kerberosUser.getPrincipal())).collect(Collectors.toSet()),
+                        "No Subject was found matching the given principal");
+                return UserGroupInformation.getUGIFromSubject(subject);
+            });
+        } catch (PrivilegedActionException e) {
+            throw new IOException("Unable to acquire UGI for KerberosUser: " + 
e.getException().getLocalizedMessage(), e.getException());
+        } catch (LoginException e) {
+            throw new IOException("Unable to acquire UGI for KerberosUser: " + 
e.getLocalizedMessage(), e);
+        }
+    }
+
     /**
      * Initializes UserGroupInformation with the given Configuration and 
returns UserGroupInformation.getLoginUser().
      * All logins should happen through this class to ensure other threads are 
not concurrently modifying
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/processors/hadoop/AbstractHadoopProcessor.java
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/processors/hadoop/AbstractHadoopProcessor.java
index a27b104..69ea716 100644
--- 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/processors/hadoop/AbstractHadoopProcessor.java
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/main/java/org/apache/nifi/processors/hadoop/AbstractHadoopProcessor.java
@@ -39,9 +39,14 @@ import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.processor.AbstractProcessor;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.util.StandardValidators;
+import org.apache.nifi.security.krb.KerberosKeytabUser;
+import org.apache.nifi.security.krb.KerberosPasswordUser;
+import org.apache.nifi.security.krb.KerberosUser;
 
 import javax.net.SocketFactory;
+import javax.security.auth.login.LoginException;
 import java.io.File;
 import java.io.IOException;
 import java.lang.ref.WeakReference;
@@ -100,8 +105,12 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
             .defaultValue(CompressionType.NONE.toString())
             .build();
 
+    /*
+     * TODO This property has been deprecated, remove for NiFi 2.0
+     */
     public static final PropertyDescriptor KERBEROS_RELOGIN_PERIOD = new 
PropertyDescriptor.Builder()
-            .name("Kerberos Relogin Period").required(false)
+            .name("Kerberos Relogin Period")
+            .required(false)
             .description("Period of time which should pass before attempting a 
kerberos relogin.\n\nThis property has been deprecated, and has no effect on 
processing. " +
                     "Relogins now occur automatically.")
             .defaultValue("4 hours")
@@ -130,6 +139,7 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
     public static final String ABSOLUTE_HDFS_PATH_ATTRIBUTE = 
"absolute.hdfs.path";
 
     private static final Object RESOURCES_LOCK = new Object();
+    private static final HdfsResources EMPTY_HDFS_RESOURCES = new 
HdfsResources(null, null, null, null);
 
     protected KerberosProperties kerberosProperties;
     protected List<PropertyDescriptor> properties;
@@ -144,7 +154,7 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
 
     @Override
     protected void init(ProcessorInitializationContext context) {
-        hdfsResources.set(new HdfsResources(null, null, null));
+        hdfsResources.set(EMPTY_HDFS_RESOURCES);
 
         kerberosConfigFile = context.getKerberosConfigurationFile();
         kerberosProperties = getKerberosProperties(kerberosConfigFile);
@@ -154,6 +164,7 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
         props.add(KERBEROS_CREDENTIALS_SERVICE);
         props.add(kerberosProperties.getKerberosPrincipal());
         props.add(kerberosProperties.getKerberosKeytab());
+        props.add(kerberosProperties.getKerberosPassword());
         props.add(KERBEROS_RELOGIN_PERIOD);
         props.add(ADDITIONAL_CLASSPATH_RESOURCES);
         properties = Collections.unmodifiableList(props);
@@ -173,10 +184,12 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
         final String configResources = 
validationContext.getProperty(HADOOP_CONFIGURATION_RESOURCES).evaluateAttributeExpressions().getValue();
         final String explicitPrincipal = 
validationContext.getProperty(kerberosProperties.getKerberosPrincipal()).evaluateAttributeExpressions().getValue();
         final String explicitKeytab = 
validationContext.getProperty(kerberosProperties.getKerberosKeytab()).evaluateAttributeExpressions().getValue();
+        final String explicitPassword = 
validationContext.getProperty(kerberosProperties.getKerberosPassword()).evaluateAttributeExpressions().getValue();
         final KerberosCredentialsService credentialsService = 
validationContext.getProperty(KERBEROS_CREDENTIALS_SERVICE).asControllerService(KerberosCredentialsService.class);
 
         final String resolvedPrincipal;
         final String resolvedKeytab;
+        final String resolvedPassword;
         if (credentialsService == null) {
             resolvedPrincipal = explicitPrincipal;
             resolvedKeytab = explicitKeytab;
@@ -184,6 +197,7 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
             resolvedPrincipal = credentialsService.getPrincipal();
             resolvedKeytab = credentialsService.getKeytab();
         }
+        resolvedPassword = explicitPassword;
 
         final List<ValidationResult> results = new ArrayList<>();
 
@@ -205,8 +219,8 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
             }
 
             final Configuration conf = resources.getConfiguration();
-            results.addAll(KerberosProperties.validatePrincipalAndKeytab(
-                this.getClass().getSimpleName(), conf, resolvedPrincipal, 
resolvedKeytab, getLogger()));
+            
results.addAll(KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                this.getClass().getSimpleName(), conf, resolvedPrincipal, 
resolvedKeytab, resolvedPassword, getLogger()));
 
         } catch (final IOException e) {
             results.add(new ValidationResult.Builder()
@@ -216,20 +230,20 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
                     .build());
         }
 
-        if (credentialsService != null && (explicitPrincipal != null || 
explicitKeytab != null)) {
+        if (credentialsService != null && (explicitPrincipal != null || 
explicitKeytab != null || explicitPassword != null)) {
             results.add(new ValidationResult.Builder()
                 .subject("Kerberos Credentials")
                 .valid(false)
-                .explanation("Cannot specify both a Kerberos Credentials 
Service and a principal/keytab")
+                .explanation("Cannot specify a Kerberos Credentials Service 
while also specifying a Kerberos Principal, Kerberos Keytab, or Kerberos 
Password")
                 .build());
         }
 
-        final String allowExplicitKeytabVariable = 
System.getenv(ALLOW_EXPLICIT_KEYTAB);
-        if ("false".equalsIgnoreCase(allowExplicitKeytabVariable) && 
(explicitPrincipal != null || explicitKeytab != null)) {
+        final String allowExplicitKeytabVariable = 
getAllowExplicitKeytabEnvironmentVariable();
+        if ("false".equalsIgnoreCase(allowExplicitKeytabVariable) && 
explicitKeytab != null) {
             results.add(new ValidationResult.Builder()
                 .subject("Kerberos Credentials")
                 .valid(false)
-                .explanation("The '" + ALLOW_EXPLICIT_KEYTAB + "' system 
environment variable is configured to forbid explicitly configuring 
principal/keytab in processors. "
+                .explanation("The '" + ALLOW_EXPLICIT_KEYTAB + "' system 
environment variable is configured to forbid explicitly configuring Kerberos 
Keytab in processors. "
                     + "The Kerberos Credentials Service should be used instead 
of setting the Kerberos Keytab or Kerberos Principal property.")
                 .build());
         }
@@ -253,7 +267,7 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
             }
         } catch (Exception ex) {
             getLogger().error("HDFS Configuration error - {}", new Object[] { 
ex });
-            hdfsResources.set(new HdfsResources(null, null, null));
+            hdfsResources.set(EMPTY_HDFS_RESOURCES);
             throw ex;
         }
     }
@@ -294,7 +308,7 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
         }
 
         // Clear out the reference to the resources
-        hdfsResources.set(new HdfsResources(null, null, null));
+        hdfsResources.set(EMPTY_HDFS_RESOURCES);
     }
 
     private void interruptStatisticsThread(final FileSystem fileSystem) throws 
NoSuchFieldException, IllegalAccessException {
@@ -371,10 +385,12 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
         // -- use RESOURCE_LOCK to guarantee UserGroupInformation is accessed 
by only a single thread at at time
         FileSystem fs;
         UserGroupInformation ugi;
+        KerberosUser kerberosUser;
         synchronized (RESOURCES_LOCK) {
             if (SecurityUtil.isSecurityEnabled(config)) {
                 String principal = 
context.getProperty(kerberosProperties.getKerberosPrincipal()).evaluateAttributeExpressions().getValue();
                 String keyTab = 
context.getProperty(kerberosProperties.getKerberosKeytab()).evaluateAttributeExpressions().getValue();
+                String password = 
context.getProperty(kerberosProperties.getKerberosPassword()).evaluateAttributeExpressions().getValue();
 
                 // If the Kerberos Credentials Service is specified, we need 
to use its configuration, not the explicit properties for principal/keytab.
                 // The customValidate method ensures that only one can be set, 
so we know that the principal & keytab above are null.
@@ -384,22 +400,29 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
                     keyTab = credentialsService.getKeytab();
                 }
 
-                ugi = SecurityUtil.loginKerberos(config, principal, keyTab);
-                fs = getFileSystemAsUser(config, ugi);
+                if (keyTab != null) {
+                    kerberosUser = new KerberosKeytabUser(principal, keyTab);
+                } else if (password != null) {
+                    kerberosUser = new KerberosPasswordUser(principal, 
password);
+                } else {
+                    throw new IOException("Unable to authenticate with 
Kerberos, no keytab or password was provided");
+                }
+                ugi = SecurityUtil.getUgiForKerberosUser(config, kerberosUser);
             } else {
                 config.set("ipc.client.fallback-to-simple-auth-allowed", 
"true");
                 config.set("hadoop.security.authentication", "simple");
                 ugi = SecurityUtil.loginSimple(config);
-                fs = getFileSystemAsUser(config, ugi);
+                kerberosUser = null;
             }
+            fs = getFileSystemAsUser(config, ugi);
         }
-        getLogger().debug("resetHDFSResources UGI {}", new Object[]{ugi});
+        getLogger().debug("resetHDFSResources UGI [{}], KerberosUser [{}]", 
new Object[]{ugi, kerberosUser});
 
         final Path workingDir = fs.getWorkingDirectory();
         getLogger().info("Initialized a new HDFS File System with working dir: 
{} default block size: {} default replication: {} config: {}",
                 new Object[]{workingDir, fs.getDefaultBlockSize(workingDir), 
fs.getDefaultReplication(workingDir), config.toString()});
 
-        return new HdfsResources(config, fs, ugi);
+        return new HdfsResources(config, fs, ugi, kerberosUser);
     }
 
     /**
@@ -518,18 +541,43 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
     }
 
     protected UserGroupInformation getUserGroupInformation() {
+        getLogger().trace("getting UGI instance");
+        if (hdfsResources.get().getKerberosUser() != null) {
+            // if there's a KerberosUser associated with this UGI, check the 
TGT and relogin if it is close to expiring
+            KerberosUser kerberosUser = hdfsResources.get().getKerberosUser();
+            synchronized (kerberosUser) {
+                getLogger().debug("kerberosUser is " + kerberosUser);
+                try {
+                    getLogger().debug("checking TGT on kerberosUser [{}]" + 
kerberosUser);
+                    kerberosUser.checkTGTAndRelogin();
+                } catch (LoginException e) {
+                    throw new ProcessException("Unable to relogin with 
kerberos credentials for " + kerberosUser.getPrincipal(), e);
+                }
+            }
+        } else {
+            getLogger().debug("kerberosUser was null, will not refresh TGT 
with KerberosUser");
+        }
         return hdfsResources.get().getUserGroupInformation();
     }
 
+    /*
+     * Overridable by subclasses in the same package, mainly intended for 
testing purposes to allow verification without having to set environment 
variables.
+     */
+    String getAllowExplicitKeytabEnvironmentVariable() {
+        return System.getenv(ALLOW_EXPLICIT_KEYTAB);
+    }
+
     static protected class HdfsResources {
         private final Configuration configuration;
         private final FileSystem fileSystem;
         private final UserGroupInformation userGroupInformation;
+        private final KerberosUser kerberosUser;
 
-        public HdfsResources(Configuration configuration, FileSystem 
fileSystem, UserGroupInformation userGroupInformation) {
+        public HdfsResources(Configuration configuration, FileSystem 
fileSystem, UserGroupInformation userGroupInformation, KerberosUser 
kerberosUser) {
             this.configuration = configuration;
             this.fileSystem = fileSystem;
             this.userGroupInformation = userGroupInformation;
+            this.kerberosUser = kerberosUser;
         }
 
         public Configuration getConfiguration() {
@@ -543,6 +591,10 @@ public abstract class AbstractHadoopProcessor extends 
AbstractProcessor {
         public UserGroupInformation getUserGroupInformation() {
             return userGroupInformation;
         }
+
+        public KerberosUser getKerberosUser() {
+            return kerberosUser;
+        }
     }
 
     static protected class ValidationResources {
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/groovy/org/apache/nifi/hadoop/SecurityUtilITSpec.groovy
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/groovy/org/apache/nifi/hadoop/SecurityUtilITSpec.groovy
new file mode 100644
index 0000000..6993961
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/groovy/org/apache/nifi/hadoop/SecurityUtilITSpec.groovy
@@ -0,0 +1,101 @@
+/*
+ * 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.hadoop
+
+import org.apache.hadoop.conf.Configuration
+import org.apache.hadoop.fs.CommonConfigurationKeysPublic
+import org.apache.hadoop.minikdc.MiniKdc
+import org.apache.nifi.security.krb.KerberosPasswordUser
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import spock.lang.Shared
+import spock.lang.Specification
+
+class SecurityUtilITSpec extends Specification {
+
+    @Shared
+    private static Logger LOGGER
+    @Shared
+    private MiniKdc miniKdc
+    @Shared
+    private Configuration configuration
+
+    def setupSpec() {
+        LOGGER = LoggerFactory.getLogger SecurityUtilITSpec
+        configuration = new Configuration()
+        configuration.set 
CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHENTICATION, "kerberos"
+        configuration.setBoolean 
CommonConfigurationKeysPublic.HADOOP_SECURITY_AUTHORIZATION, true
+        configuration.setInt 
CommonConfigurationKeysPublic.HADOOP_KERBEROS_MIN_SECONDS_BEFORE_RELOGIN, 5
+        def miniKdcConf = MiniKdc.createConf()
+        miniKdcConf.setProperty MiniKdc.INSTANCE, 
InetAddress.localHost.hostName
+        miniKdcConf.setProperty MiniKdc.ORG_NAME, "EXAMPLE"
+        miniKdcConf.setProperty MiniKdc.ORG_DOMAIN, "COM"
+        miniKdcConf.setProperty MiniKdc.DEBUG, "false"
+        miniKdcConf.setProperty MiniKdc.MAX_TICKET_LIFETIME, "5"
+        miniKdc = new MiniKdc(miniKdcConf, new File("./target/minikdc"))
+        miniKdc.start()
+    }
+
+    def cleanupSpec() {
+        miniKdc.stop()
+    }
+
+    def "getUgiForKerberosUser with unauthenticated KerberosPasswordUser 
returns correct UGI"() {
+        given:
+        def principal = "testprincipal2"
+        def password = "password"
+        miniKdc.createPrincipal principal, password
+
+        when: "A KerberosPasswordUser is created for the given principal"
+        def kerberosPasswordUser = new KerberosPasswordUser(principal, 
password)
+
+        then: "The created KerberosPasswordUser is not logged in"
+        !kerberosPasswordUser.isLoggedIn()
+
+        when: "A UGI is acquired for the KerberosPasswordUser"
+        def ugi = SecurityUtil.getUgiForKerberosUser configuration, 
kerberosPasswordUser
+
+        then: "The KerberosPasswordUser is logged in and the acquired UGI is 
valid for the given principal"
+        kerberosPasswordUser.isLoggedIn()
+        ugi != null
+        ugi.getShortUserName() == principal
+        LOGGER.debug "UGI = [{}], KerberosUser = [{}]", ugi, 
kerberosPasswordUser
+    }
+
+    def "getUgiForKerberosUser with authenticated KerberosPasswordUser returns 
correct UGI"() {
+        given:
+        def principal = "testprincipal3"
+        def password = "password"
+        miniKdc.createPrincipal principal, password
+
+        when: "A KerberosPasswordUser is created for the given principal and 
authenticated"
+        def kerberosPasswordUser = new KerberosPasswordUser(principal, 
password)
+        kerberosPasswordUser.login()
+
+        then: "The created KerberosPasswordUser is logged in"
+        kerberosPasswordUser.isLoggedIn()
+
+        when: "A UGI is acquired for the KerberosPasswordUser"
+        def ugi = SecurityUtil.getUgiForKerberosUser configuration, 
kerberosPasswordUser
+
+        then: "The KerberosPasswordUser is logged in and the acquired UGI is 
valid for the given principal"
+        kerberosPasswordUser.isLoggedIn()
+        ugi != null
+        ugi.getShortUserName() == principal
+        LOGGER.debug "UGI = [{}], KerberosUser = [{}]", ugi, 
kerberosPasswordUser
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/groovy/org/apache/nifi/processors/hadoop/AbstractHadoopProcessorSpec.groovy
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/groovy/org/apache/nifi/processors/hadoop/AbstractHadoopProcessorSpec.groovy
new file mode 100644
index 0000000..9b4faf5
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/groovy/org/apache/nifi/processors/hadoop/AbstractHadoopProcessorSpec.groovy
@@ -0,0 +1,113 @@
+/*
+ * 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.processors.hadoop
+
+import org.apache.nifi.components.PropertyValue
+import org.apache.nifi.components.ValidationContext
+import org.apache.nifi.hadoop.KerberosProperties
+import org.apache.nifi.kerberos.KerberosCredentialsService
+import org.apache.nifi.processor.ProcessContext
+import org.apache.nifi.processor.ProcessSession
+import org.apache.nifi.processor.ProcessorInitializationContext
+import org.apache.nifi.processor.exception.ProcessException
+import org.apache.nifi.util.MockComponentLog
+import org.apache.nifi.util.MockPropertyValue
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+import spock.lang.Specification
+import spock.lang.Unroll
+
+class AbstractHadoopProcessorSpec extends Specification {
+    private static Logger LOGGER = LoggerFactory.getLogger 
AbstractHadoopProcessorSpec
+
+    @Unroll
+    def "customValidate for #testName"() {
+        given:
+        def testAbstractHadoopProcessor = new 
TestAbstractHadoopProcessor(allowExplicitKeytab);
+        testAbstractHadoopProcessor.kerberosProperties = new 
KerberosProperties(new File('src/test/resources/krb5.conf'))
+        def mockProcessorInitializationContext = Mock 
ProcessorInitializationContext
+        def mockValidationContext = Mock ValidationContext
+        def mockHadoopConfigurationResourcesPropertyValue = Mock PropertyValue
+        def mockKerberosCredentialsServicePropertyValue = Mock PropertyValue
+        def mockKerberosCredentialsServiceControllerService = Mock 
KerberosCredentialsService
+
+        when:
+        
testAbstractHadoopProcessor.initialize(mockProcessorInitializationContext)
+        def validationResults = 
testAbstractHadoopProcessor.customValidate(mockValidationContext)
+
+        then:
+        1 * mockProcessorInitializationContext.getLogger() >> new 
MockComponentLog("AbstractHadoopProcessorSpec", testAbstractHadoopProcessor)
+        1 * 
mockValidationContext.getProperty(AbstractHadoopProcessor.HADOOP_CONFIGURATION_RESOURCES)
 >> mockHadoopConfigurationResourcesPropertyValue
+        1 * 
mockHadoopConfigurationResourcesPropertyValue.evaluateAttributeExpressions() >> 
mockHadoopConfigurationResourcesPropertyValue
+        1 * mockHadoopConfigurationResourcesPropertyValue.getValue() >> 
"src/test/resources/test-secure-core-site.xml"
+
+        1 * 
mockValidationContext.getProperty(AbstractHadoopProcessor.KERBEROS_CREDENTIALS_SERVICE)
 >> mockKerberosCredentialsServicePropertyValue
+        if (configuredKeytabCredentialsService) {
+            1 * 
mockKerberosCredentialsServicePropertyValue.asControllerService(KerberosCredentialsService.class)
 >> mockKerberosCredentialsServiceControllerService
+            1 * mockKerberosCredentialsServiceControllerService.principal >> 
configuredKeytabCredentialsServicePrincipal
+            1 * mockKerberosCredentialsServiceControllerService.keytab >> 
configuredKeytabCredentialsServiceKeytab
+        }
+
+        1 * 
mockValidationContext.getProperty(testAbstractHadoopProcessor.kerberosProperties.kerberosPrincipal)
 >> new MockPropertyValue(configuredPrincipal)
+        1 * 
mockValidationContext.getProperty(testAbstractHadoopProcessor.kerberosProperties.kerberosPassword)
 >> new MockPropertyValue(configuredPassword)
+        1 * 
mockValidationContext.getProperty(testAbstractHadoopProcessor.kerberosProperties.kerberosKeytab)
 >> new MockPropertyValue(configuredKeytab)
+
+        then:
+        def actualValidationErrors = validationResults.each { !it.isValid() }
+        if (actualValidationErrors.size() > 0) {
+            actualValidationErrors.each { LOGGER.debug(it.toString()) }
+        }
+        actualValidationErrors.size() == expectedValidationErrorCount
+
+        where:
+        testName | configuredPrincipal | configuredKeytab | configuredPassword 
| allowExplicitKeytab | configuredKeytabCredentialsService | 
configuredKeytabCredentialsServicePrincipal | 
configuredKeytabCredentialsServiceKeytab || expectedValidationErrorCount
+        "success case 1"       | "principal"         | "keytab"         | null 
              | "true"              | false                              | null 
                                       | null                                   
  || 0
+        "success case 2"       | "principal"         | null             | 
"password"         | "true"              | false                              | 
null                                        | null                              
       || 0
+        "success case 3"       | "principal"         | null             | 
"password"         | "false"             | false                              | 
null                                        | null                              
       || 0
+        "success case 4"       | null                | null             | null 
              | "true"              | true                               | 
"principal"                                 | "keytab"                          
       || 0
+        "success case 5"       | null                | null             | null 
              | "false"             | true                               | 
"principal"                                 | "keytab"                          
       || 0
+        // do not allow explicit keytab, but provide one anyway; validation 
fails
+        "failure case 1"       | "principal"         | "keytab"         | null 
              | "false"             | false                              | null 
                                       | null                                   
  || 1
+        "failure case 2"       | null                | "keytab"         | null 
              | "false"             | false                              | null 
                                       | null                                   
  || 2
+        // keytab credentials service is provided, but explicit properties for 
principal, password, or keytab are also provided; validation fails
+        "failure case 3"       | "principal"         | null             | null 
              | "true"              | true                               | 
"principal"                                 | "keytab"                          
       || 1
+        "failure case 4"       | null                | "keytab"         | null 
              | "true"              | true                               | 
"principal"                                 | "keytab"                          
       || 1
+        "failure case 5"       | null                | null             | 
"password"         | "true"              | true                               | 
"principal"                                 | "keytab"                          
       || 2
+        "failure case 6"       | "principal"         | null             | null 
              | "false"             | true                               | 
"principal"                                 | "keytab"                          
       || 1
+        "failure case 7"       | null                | "keytab"         | null 
              | "false"             | true                               | 
"principal"                                 | "keytab"                          
       || 2
+        "failure case 8"       | null                | null             | 
"password"         | "false"             | true                               | 
"principal"                                 | "keytab"                          
       || 2
+    }
+
+    private class TestAbstractHadoopProcessor extends AbstractHadoopProcessor {
+        def allowExplicitKeytab = false
+
+        TestAbstractHadoopProcessor(def allowExplicitKeytab) {
+            this.allowExplicitKeytab = allowExplicitKeytab
+        }
+
+        @Override
+        void onTrigger(ProcessContext context, ProcessSession session) throws 
ProcessException {
+            throw new NoSuchMethodError("not intended to be invoked by the 
test, this implementation is only intended for custom validation purposes")
+        }
+
+        @Override
+        String getAllowExplicitKeytabEnvironmentVariable() {
+            allowExplicitKeytab
+        }
+
+    }
+}
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/java/org/apache/nifi/hadoop/TestKerberosProperties.java
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/java/org/apache/nifi/hadoop/TestKerberosProperties.java
index 8cd1ea1..288c1be 100644
--- 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/java/org/apache/nifi/hadoop/TestKerberosProperties.java
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/java/org/apache/nifi/hadoop/TestKerberosProperties.java
@@ -65,26 +65,70 @@ public class TestKerberosProperties {
         final ComponentLog log = Mockito.mock(ComponentLog.class);
         final Configuration config = new Configuration();
 
-        // no security enabled in config so doesn't matter what principal and 
keytab are
-        List<ValidationResult> results = 
KerberosProperties.validatePrincipalAndKeytab(
-                "test", config, null, null, log);
+        // no security enabled in config so doesn't matter what principal, 
keytab, and password are
+        List<ValidationResult> results = 
KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, null, null, null, log);
         Assert.assertEquals(0, results.size());
 
-        results = KerberosProperties.validatePrincipalAndKeytab(
-                "test", config, "principal", null, log);
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", null, null, log);
         Assert.assertEquals(0, results.size());
 
-        results = KerberosProperties.validatePrincipalAndKeytab(
-                "test", config, "principal", "keytab", log);
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", "keytab", null, log);
+        Assert.assertEquals(0, results.size());
+
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", null, "password", log);
+        Assert.assertEquals(0, results.size());
+
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", "keytab", "password", log);
         Assert.assertEquals(0, results.size());
 
         // change the config to have kerberos turned on
         config.set("hadoop.security.authentication", "kerberos");
         config.set("hadoop.security.authorization", "true");
 
-        results = KerberosProperties.validatePrincipalAndKeytab(
-                "test", config, null, null, log);
+        // security is enabled, no principal, keytab, or password provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, null, null, null, log);
+        Assert.assertEquals(2, results.size());
+
+        // security is enabled, keytab provided, no principal or password 
provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, null, "keytab", null, log);
+        Assert.assertEquals(1, results.size());
+
+        // security is enabled, password provided, no principal or keytab 
provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, null, null, "password", log);
+        Assert.assertEquals(1, results.size());
+
+        // security is enabled, no principal provided, keytab and password 
provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, null, "keytab", "password", log);
         Assert.assertEquals(2, results.size());
+
+        // security is enabled, principal provided, no keytab or password 
provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", null, null, log);
+        Assert.assertEquals(1, results.size());
+
+        // security is enabled, principal and keytab provided, no password 
provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", "keytab", null, log);
+        Assert.assertEquals(0, results.size());
+
+        // security is enabled, no keytab provided, principal and password 
provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", null, "password", log);
+        Assert.assertEquals(0, results.size());
+
+        // security is enabled, principal, keytab, and password provided
+        results = KerberosProperties.validatePrincipalWithKeytabOrPassword(
+                "test", config, "principal", "keytab", "password", log);
+        Assert.assertEquals(1, results.size());
     }
 
 }
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/resources/log4j.properties
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/resources/log4j.properties
new file mode 100644
index 0000000..cdebe46
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/resources/log4j.properties
@@ -0,0 +1,30 @@
+#
+# 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.
+
+# Root logger option
+log4j.rootLogger=WARN, stdout
+
+log4j.logger.org.apache.nifi=INFO
+log4j.logger.org.apache.hadoop.security=INFO
+log4j.logger.org.apache.hadoop.minikdc=WARN
+log4j.logger.org.apache.nifi.hadoop.SecurityUtilITSpec=DEBUG
+log4j.logger.org.apache.nifi.processors.hadoop.AbstractHadoopProcessorSpec=DEBUG
+
+# log messages to console
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.Target=System.out
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %-5p [%t] %40.40c - %m%n
diff --git 
a/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/resources/test-secure-core-site.xml
 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/resources/test-secure-core-site.xml
new file mode 100644
index 0000000..1ff71c7
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-extension-utils/nifi-hadoop-utils/src/test/resources/test-secure-core-site.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  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.
+-->
+<configuration>
+    <property>
+        <name>hadoop.security.authentication</name>
+        <value>kerberos</value>
+    </property>
+</configuration>
\ No newline at end of file
diff --git 
a/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/test/java/org/apache/nifi/processors/hadoop/GetHDFSSequenceFileTest.java
 
b/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/test/java/org/apache/nifi/processors/hadoop/GetHDFSSequenceFileTest.java
index 69d1acd..6027f8e 100644
--- 
a/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/test/java/org/apache/nifi/processors/hadoop/GetHDFSSequenceFileTest.java
+++ 
b/nifi-nar-bundles/nifi-hadoop-bundle/nifi-hdfs-processors/src/test/java/org/apache/nifi/processors/hadoop/GetHDFSSequenceFileTest.java
@@ -25,6 +25,7 @@ import org.apache.nifi.hadoop.KerberosProperties;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessorInitializationContext;
 import org.apache.nifi.processors.hadoop.util.SequenceFileReader;
+import org.apache.nifi.util.MockComponentLog;
 import org.apache.nifi.util.MockProcessContext;
 import org.junit.Before;
 import org.junit.Test;
@@ -38,6 +39,7 @@ import static org.junit.Assert.assertFalse;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 public class GetHDFSSequenceFileTest {
     private AbstractHadoopProcessor.HdfsResources hdfsResources;
@@ -52,7 +54,7 @@ public class GetHDFSSequenceFileTest {
         configuration = mock(Configuration.class);
         fileSystem = mock(FileSystem.class);
         userGroupInformation = mock(UserGroupInformation.class);
-        hdfsResources = new 
AbstractHadoopProcessor.HdfsResources(configuration, fileSystem, 
userGroupInformation);
+        hdfsResources = new 
AbstractHadoopProcessor.HdfsResources(configuration, fileSystem, 
userGroupInformation, null);
         getHDFSSequenceFile = new TestableGetHDFSSequenceFile();
         getHDFSSequenceFile.kerberosProperties = 
mock(KerberosProperties.class);
         reloginTried = false;
@@ -61,7 +63,10 @@ public class GetHDFSSequenceFileTest {
 
     private void init() throws IOException {
         final MockProcessContext context = new 
MockProcessContext(getHDFSSequenceFile);
-        getHDFSSequenceFile.init(mock(ProcessorInitializationContext.class));
+        ProcessorInitializationContext mockProcessorInitializationContext = 
mock(ProcessorInitializationContext.class);
+        when(mockProcessorInitializationContext.getLogger()).thenReturn(new 
MockComponentLog("GetHDFSSequenceFileTest", getHDFSSequenceFile ));
+        getHDFSSequenceFile.initialize(mockProcessorInitializationContext);
+        getHDFSSequenceFile.init(mockProcessorInitializationContext);
         getHDFSSequenceFile.onScheduled(context);
     }
 
@@ -80,7 +85,7 @@ public class GetHDFSSequenceFileTest {
 
     @Test
     public void testGetFlowFilesNoUgiShouldntCallDoAs() throws Exception {
-        hdfsResources = new 
AbstractHadoopProcessor.HdfsResources(configuration, fileSystem, null);
+        hdfsResources = new 
AbstractHadoopProcessor.HdfsResources(configuration, fileSystem, null, null);
         init();
         SequenceFileReader reader = mock(SequenceFileReader.class);
         Path file = mock(Path.class);
diff --git 
a/nifi-nar-bundles/nifi-hive-bundle/nifi-hive-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
 
b/nifi-nar-bundles/nifi-hive-bundle/nifi-hive-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
index a987ff8..5fed81a 100644
--- 
a/nifi-nar-bundles/nifi-hive-bundle/nifi-hive-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
+++ 
b/nifi-nar-bundles/nifi-hive-bundle/nifi-hive-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
@@ -50,7 +50,7 @@ public class HiveConfigurator {
 
         final Configuration hiveConfig = resources.getConfiguration();
 
-        
problems.addAll(KerberosProperties.validatePrincipalAndKeytab(this.getClass().getSimpleName(),
 hiveConfig, principal, keyTab, log));
+        
problems.addAll(KerberosProperties.validatePrincipalWithKeytabOrPassword(this.getClass().getSimpleName(),
 hiveConfig, principal, keyTab, null, log));
 
         return problems;
     }
diff --git 
a/nifi-nar-bundles/nifi-hive-bundle/nifi-hive3-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
 
b/nifi-nar-bundles/nifi-hive-bundle/nifi-hive3-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
index 6d53683..092557b 100644
--- 
a/nifi-nar-bundles/nifi-hive-bundle/nifi-hive3-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
+++ 
b/nifi-nar-bundles/nifi-hive-bundle/nifi-hive3-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
@@ -53,7 +53,7 @@ public class HiveConfigurator {
 
         final Configuration hiveConfig = resources.getConfiguration();
 
-        
problems.addAll(KerberosProperties.validatePrincipalAndKeytab(this.getClass().getSimpleName(),
 hiveConfig, principal, keyTab, log));
+        
problems.addAll(KerberosProperties.validatePrincipalWithKeytabOrPassword(this.getClass().getSimpleName(),
 hiveConfig, principal, keyTab, null, log));
 
         return problems;
     }
diff --git 
a/nifi-nar-bundles/nifi-hive-bundle/nifi-hive_1_1-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
 
b/nifi-nar-bundles/nifi-hive-bundle/nifi-hive_1_1-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
index a987ff8..5fed81a 100644
--- 
a/nifi-nar-bundles/nifi-hive-bundle/nifi-hive_1_1-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
+++ 
b/nifi-nar-bundles/nifi-hive-bundle/nifi-hive_1_1-processors/src/main/java/org/apache/nifi/util/hive/HiveConfigurator.java
@@ -50,7 +50,7 @@ public class HiveConfigurator {
 
         final Configuration hiveConfig = resources.getConfiguration();
 
-        
problems.addAll(KerberosProperties.validatePrincipalAndKeytab(this.getClass().getSimpleName(),
 hiveConfig, principal, keyTab, log));
+        
problems.addAll(KerberosProperties.validatePrincipalWithKeytabOrPassword(this.getClass().getSimpleName(),
 hiveConfig, principal, keyTab, null, log));
 
         return problems;
     }
diff --git 
a/nifi-nar-bundles/nifi-standard-services/nifi-hbase_1_1_2-client-service-bundle/nifi-hbase_1_1_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_1_1_2_ClientService.java
 
b/nifi-nar-bundles/nifi-standard-services/nifi-hbase_1_1_2-client-service-bundle/nifi-hbase_1_1_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_1_1_2_ClientService.java
index 052b82b..fe2af63 100644
--- 
a/nifi-nar-bundles/nifi-standard-services/nifi-hbase_1_1_2-client-service-bundle/nifi-hbase_1_1_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_1_1_2_ClientService.java
+++ 
b/nifi-nar-bundles/nifi-standard-services/nifi-hbase_1_1_2-client-service-bundle/nifi-hbase_1_1_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_1_1_2_ClientService.java
@@ -261,7 +261,7 @@ public class HBase_1_1_2_ClientService extends 
AbstractControllerService impleme
 
             final Configuration hbaseConfig = resources.getConfiguration();
 
-            
problems.addAll(KerberosProperties.validatePrincipalAndKeytab(getClass().getSimpleName(),
 hbaseConfig, resolvedPrincipal, resolvedKeytab, getLogger()));
+            
problems.addAll(KerberosProperties.validatePrincipalWithKeytabOrPassword(getClass().getSimpleName(),
 hbaseConfig, resolvedPrincipal, resolvedKeytab, null, getLogger()));
         }
 
         if (credentialsService != null && (explicitPrincipal != null || 
explicitKeytab != null)) {
diff --git 
a/nifi-nar-bundles/nifi-standard-services/nifi-hbase_2-client-service-bundle/nifi-hbase_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_2_ClientService.java
 
b/nifi-nar-bundles/nifi-standard-services/nifi-hbase_2-client-service-bundle/nifi-hbase_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_2_ClientService.java
index bc41f76..59f7312 100644
--- 
a/nifi-nar-bundles/nifi-standard-services/nifi-hbase_2-client-service-bundle/nifi-hbase_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_2_ClientService.java
+++ 
b/nifi-nar-bundles/nifi-standard-services/nifi-hbase_2-client-service-bundle/nifi-hbase_2-client-service/src/main/java/org/apache/nifi/hbase/HBase_2_ClientService.java
@@ -260,7 +260,7 @@ public class HBase_2_ClientService extends 
AbstractControllerService implements
 
             final Configuration hbaseConfig = resources.getConfiguration();
 
-            
problems.addAll(KerberosProperties.validatePrincipalAndKeytab(getClass().getSimpleName(),
 hbaseConfig, resolvedPrincipal, resolvedKeytab, getLogger()));
+            
problems.addAll(KerberosProperties.validatePrincipalWithKeytabOrPassword(getClass().getSimpleName(),
 hbaseConfig, resolvedPrincipal, resolvedKeytab, null, getLogger()));
         }
 
         if (credentialsService != null && (explicitPrincipal != null || 
explicitKeytab != null)) {

Reply via email to