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

thenatog pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 1c361d4  NIFI-6999 - Made changes to load flow.xml files using 
streams. Updated tests.
1c361d4 is described below

commit 1c361d45ae94f155b6e2def7bd4430b1c9ca8b3b
Author: Nathan Gough <[email protected]>
AuthorDate: Fri Dec 4 19:05:52 2020 -0500

    NIFI-6999 - Made changes to load flow.xml files using streams. Updated 
tests.
    
    NIFI-6999 - Slight change to test to check for WARN message.
    
    NIFI-6999 - Removed very large flow file and test that uses it. This test 
ran for about 2 minutes so was excessive to keep in. The other changed tests to 
handle streams proves the functionality. A large file can be used on the 
command line to manually test large flow files. Some other cleanup.
    
    NIFI-6999 - Removed comments and altered the code a little bit for 
readability as per code review.
    
    NIFI-6999 - Removed commented code
    
    NIFI-6999 - Renamed variable and removed assert comment.
    
    Signed-off-by: Nathan Gough <[email protected]>
    
    This closes #4715.
---
 .../nifi/properties/ConfigEncryptionTool.groovy    | 107 +++---
 .../properties/ConfigEncryptionToolTest.groovy     | 376 ++++++++++++++-------
 .../src/test/resources/flow.xml                    |   4 +-
 .../src/test/resources/flow.xml.gz                 | Bin 1674 -> 1652 bytes
 .../src/test/resources/flow_default_key.xml        |   4 +-
 .../src/test/resources/flow_default_key.xml.gz     | Bin 1686 -> 1654 bytes
 6 files changed, 322 insertions(+), 169 deletions(-)

diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
index 2a10043..919687f 100644
--- 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/properties/ConfigEncryptionTool.groovy
@@ -27,7 +27,6 @@ import org.apache.commons.cli.Option
 import org.apache.commons.cli.Options
 import org.apache.commons.cli.ParseException
 import org.apache.commons.codec.binary.Hex
-import org.apache.commons.io.IOUtils
 import org.apache.nifi.security.kms.CryptoUtils
 import org.apache.nifi.toolkit.tls.commandLine.CommandLineParseException
 import org.apache.nifi.toolkit.tls.commandLine.ExitCode
@@ -47,12 +46,17 @@ import javax.crypto.SecretKeyFactory
 import javax.crypto.spec.PBEKeySpec
 import javax.crypto.spec.PBEParameterSpec
 import java.nio.charset.StandardCharsets
+import java.nio.file.Path
+import java.nio.file.Paths
+import java.nio.file.StandardCopyOption;
 import java.security.KeyException
 import java.security.SecureRandom
 import java.security.Security
 import java.util.regex.Matcher
 import java.util.zip.GZIPInputStream
 import java.util.zip.GZIPOutputStream
+import java.util.zip.ZipException
+import java.nio.file.Files;
 
 class ConfigEncryptionTool {
     private static final Logger logger = 
LoggerFactory.getLogger(ConfigEncryptionTool.class)
@@ -64,8 +68,8 @@ class ConfigEncryptionTool {
     public String outputLoginIdentityProvidersPath
     public String authorizersPath
     public String outputAuthorizersPath
-    public String flowXmlPath
-    public String outputFlowXmlPath
+    public static flowXmlPath
+    public outputFlowXmlPath
 
     private String keyHex
     private String migrationKeyHex
@@ -81,7 +85,7 @@ class ConfigEncryptionTool {
     private NiFiProperties niFiProperties
     private String loginIdentityProviders
     private String authorizers
-    private String flowXml
+    private InputStream flowXmlInputStream
 
     private boolean usingPassword = true
     private boolean usingPasswordMigration = true
@@ -657,26 +661,24 @@ class ConfigEncryptionTool {
     /**
      * Loads the flow definition from the provided file path, handling the 
GZIP file compression. Unlike {@link #loadLoginIdentityProviders()} this method 
does not decrypt the content (for performance and separation of concern 
reasons).
      *
-     * @return the file content
+     * @param The path to the XML file
+     * @return The file content
      * @throw IOException if the flow.xml.gz file cannot be read
      */
-    private String loadFlowXml() throws IOException {
-        if (flowXmlPath && (new File(flowXmlPath)).exists()) {
+    private InputStream loadFlowXml(String filePath) throws IOException {
+        if (filePath && (new File(filePath)).exists()) {
             try {
-                new FileInputStream(flowXmlPath).withCloseable {
-                    new GZIPInputStream(it).withCloseable {
-                        String xmlContent = IOUtils.toString(it, 
StandardCharsets.UTF_8)
-                        return xmlContent
-                    }
-                }
+                return new GZIPInputStream(new FileInputStream(filePath))
+            } catch (ZipException e) {
+                return new FileInputStream(filePath)
             } catch (RuntimeException e) {
                 if (isVerbose) {
                     logger.error("Encountered an error", e)
                 }
-                throw new IOException("Cannot load flow from 
[${flowXmlPath}]", e)
+                throw new IOException("Cannot load flow from [${filePath}]", e)
             }
         } else {
-            printUsageAndThrow("Cannot load flow from [${flowXmlPath}]", 
ExitCode.ERROR_READING_NIFI_PROPERTIES)
+            printUsageAndThrow("Cannot load flow from [${filePath}]", 
ExitCode.ERROR_READING_NIFI_PROPERTIES)
         }
     }
 
@@ -790,14 +792,14 @@ class ConfigEncryptionTool {
     /**
      * Scans XML content and decrypts each encrypted element, then re-encrypts 
it with the new key, and returns the final XML content.
      *
-     * @param flowXmlContent the original flow.xml.gz content
+     * @param flowXmlContent the original flow.xml.gz as an input stream
      * @param existingFlowPassword the existing value of 
nifi.sensitive.props.key (not a raw key, but rather a password)
      * @param newFlowPassword the password to use to for encryption (not a raw 
key, but rather a password)
      * @param existingAlgorithm the KDF algorithm to use (defaults to 
PBEWITHMD5AND256BITAES-CBC-OPENSSL)
      * @param existingProvider the {@link java.security.Provider} to use 
(defaults to BC)
-     * @return the encrypted XML content
+     * @return the encrypted XML content as an InputStream
      */
-    private String migrateFlowXmlContent(String flowXmlContent, String 
existingFlowPassword, String newFlowPassword, String existingAlgorithm = 
DEFAULT_FLOW_ALGORITHM, String existingProvider = DEFAULT_PROVIDER, String 
newAlgorithm = DEFAULT_FLOW_ALGORITHM, String newProvider = DEFAULT_PROVIDER) {
+    private InputStream migrateFlowXmlContent(InputStream flowXmlContent, 
String existingFlowPassword, String newFlowPassword, String existingAlgorithm = 
DEFAULT_FLOW_ALGORITHM, String existingProvider = DEFAULT_PROVIDER, String 
newAlgorithm = DEFAULT_FLOW_ALGORITHM, String newProvider = DEFAULT_PROVIDER) {
         /* For re-encryption, for performance reasons, we will use a fixed 
salt for all of
          * the operations. These values are stored in the same file and the 
default key is in the
          * source code (see NIFI-1465 and NIFI-1277), so the security 
trade-off is minimal
@@ -810,21 +812,51 @@ class ConfigEncryptionTool {
         Cipher encryptCipher = generateFlowEncryptionCipher(newFlowPassword, 
encryptionSalt, newAlgorithm, newProvider)
 
         int elementCount = 0
-
-        // Scan the XML content and identify every encrypted element, decrypt 
it, and replace it with the re-encrypted value
-        String migratedFlowXmlContent = 
flowXmlContent.replaceAll(WRAPPED_FLOW_XML_CIPHER_TEXT_REGEX) { String 
wrappedCipherText ->
-            String plaintext = decryptFlowElement(wrappedCipherText, 
existingFlowPassword, existingAlgorithm, existingProvider)
-            byte[] cipherBytes = encryptCipher.doFinal(plaintext.bytes)
-            byte[] saltAndCipherBytes = concatByteArrays(encryptionSalt, 
cipherBytes)
-            elementCount++
-            "enc{${Hex.encodeHex(saltAndCipherBytes)}}"
+        File tempFlowXmlFile = new 
File(getTemporaryFlowXmlFile(outputFlowXmlPath).toString())
+        BufferedWriter tempFlowXmlWriter = 
getFlowOutputStream(tempFlowXmlFile, flowXmlContent instanceof GZIPInputStream)
+
+        // Scan through XML content as a stream, decrypt and re-encrypt fields 
with a new flow password
+        final BufferedReader reader = new BufferedReader(new 
InputStreamReader(flowXmlContent))
+        String line;
+
+        while((line = reader.readLine()) != null) {
+            def matcher = line =~ WRAPPED_FLOW_XML_CIPHER_TEXT_REGEX
+            if(matcher.find()) {
+                String plaintext = decryptFlowElement(matcher.getAt(0), 
existingFlowPassword, existingAlgorithm, existingProvider)
+                byte[] cipherBytes = encryptCipher.doFinal(plaintext.bytes)
+                byte[] saltAndCipherBytes = concatByteArrays(encryptionSalt, 
cipherBytes)
+                elementCount++
+                
tempFlowXmlWriter.writeLine(line.replaceFirst(WRAPPED_FLOW_XML_CIPHER_TEXT_REGEX,
 "enc{${Hex.encodeHex(saltAndCipherBytes)}}"))
+            } else {
+                tempFlowXmlWriter.writeLine(line)
+            }
         }
+        tempFlowXmlWriter.flush()
+        tempFlowXmlWriter.close()
+
+        // Overwrite the original flow file with the migrated flow file
+        Files.move(tempFlowXmlFile.toPath(), Paths.get(outputFlowXmlPath), 
StandardCopyOption.ATOMIC_MOVE)
 
         if (isVerbose) {
             logger.info("Decrypted and re-encrypted ${elementCount} elements 
for flow.xml.gz")
         }
 
-        migratedFlowXmlContent
+        loadFlowXml(outputFlowXmlPath)
+    }
+
+    private BufferedWriter getFlowOutputStream(File outputFlowXmlPath, boolean 
isFileGZipped) {
+        OutputStream flowOutputStream = new FileOutputStream(outputFlowXmlPath)
+        if(isFileGZipped) {
+            flowOutputStream = new GZIPOutputStream(flowOutputStream)
+        }
+        new BufferedWriter(new OutputStreamWriter(flowOutputStream))
+    }
+
+    // Create a temporary output file we can write the stream to
+    private Path getTemporaryFlowXmlFile(String originalOutputFlowXmlPath) {
+        String outputFilename = 
Paths.get(originalOutputFlowXmlPath).getFileName().toString()
+        String migratedFileName = "migrated-${outputFilename}"
+        Paths.get(originalOutputFlowXmlPath).resolveSibling(migratedFileName)
     }
 
     /**
@@ -861,19 +893,6 @@ class ConfigEncryptionTool {
         encryptCipher
     }
 
-    /**
-     * Writes the XML content to the {@link .outputFlowXmlPath} location, 
handling the GZIP file compression.
-     *
-     * @param flowXmlContent the XML content to write
-     */
-    private void writeFlowXmlToFile(String flowXmlContent) {
-        new FileOutputStream(outputFlowXmlPath).withCloseable {
-            new GZIPOutputStream(it).withCloseable {
-                IOUtils.write(flowXmlContent, it, StandardCharsets.UTF_8)
-            }
-        }
-    }
-
     String decryptLoginIdentityProviders(String encryptedXml, String 
existingKeyHex = keyHex) {
         AESSensitivePropertyProvider sensitivePropertyProvider = new 
AESSensitivePropertyProvider(existingKeyHex)
 
@@ -1536,14 +1555,13 @@ class ConfigEncryptionTool {
 
                 if (tool.handlingFlowXml) {
                     try {
-                        tool.flowXml = tool.loadFlowXml()
+                        tool.flowXmlInputStream = tool.loadFlowXml(flowXmlPath)
                     } catch (Exception e) {
                         if (tool.isVerbose) {
                             logger.error("Encountered an error: ", e)
                         }
                         tool.printUsageAndThrow("Cannot load flow.xml.gz", 
ExitCode.ERROR_READING_NIFI_PROPERTIES)
                     }
-                    tool.handleFlowXml(existingNiFiPropertiesAreEncrypted)
                 }
 
                 if (tool.handlingNiFiProperties) {
@@ -1568,7 +1586,7 @@ class ConfigEncryptionTool {
                         tool.writeKeyToBootstrapConf()
                     }
                     if (tool.handlingFlowXml) {
-                        tool.writeFlowXmlToFile(tool.flowXml)
+                        tool.handleFlowXml(tool.niFiPropertiesAreEncrypted())
                     }
                     if (tool.handlingNiFiProperties || tool.handlingFlowXml) {
                         tool.writeNiFiProperties()
@@ -1612,7 +1630,8 @@ class ConfigEncryptionTool {
         String newProvider = newFlowProvider ?: existingProvider
 
         try {
-            flowXml = migrateFlowXmlContent(flowXml, existingFlowPassword, 
newFlowPassword, existingAlgorithm, existingProvider, newAlgorithm, newProvider)
+            logger.info("Migrating flow.xml file at ${flowXmlPath}. This could 
take a while if the flow XML is very large.")
+            migrateFlowXmlContent(flowXmlInputStream, existingFlowPassword, 
newFlowPassword, existingAlgorithm, existingProvider, newAlgorithm, newProvider)
         } catch (Exception e) {
             logger.error("Encountered an error: ${e.getLocalizedMessage()}")
             if (e instanceof BadPaddingException) {
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
index 9a7f08a..a3c5d10 100644
--- 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/groovy/org/apache/nifi/properties/ConfigEncryptionToolTest.groovy
@@ -60,6 +60,9 @@ import java.nio.file.attribute.PosixFilePermission
 import java.security.KeyException
 import java.security.Security
 
+import org.apache.commons.io.IOUtils
+import java.nio.charset.StandardCharsets
+
 @RunWith(JUnit4.class)
 class ConfigEncryptionToolTest extends GroovyTestCase {
     private static final Logger logger = 
LoggerFactory.getLogger(ConfigEncryptionToolTest.class)
@@ -77,6 +80,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     private static final String KEY_HEX_256 = KEY_HEX_128 * 2
     public static final String KEY_HEX = isUnlimitedStrengthCryptoAvailable() 
? KEY_HEX_256 : KEY_HEX_128
     private static final String PASSWORD = "thisIsABadPassword"
+    private static final String ANOTHER_PASSWORD = "thisIsAnotherBadPassword"
 
     private static final String STATIC_SALT = 
"\$s0\$40801\$ABCDEFGHIJKLMNOPQRSTUV"
     private static final String SCRYPT_SALT_PATTERN = 
/\$\w{2}\$\w{5,}\$[\w\/\=\+]+/
@@ -287,7 +291,9 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
 
         // Assert
         assert !TestAppender.events.isEmpty()
-        assert TestAppender.events.first().message =~ "The source 
nifi.properties and destination nifi.properties are identical \\[.*\\] so the 
original will be overwritten"
+        assert TestAppender.events.stream().any() {
+            it.message =~ "The source nifi.properties and destination 
nifi.properties are identical \\[.*\\] so the original will be overwritten"
+        }
     }
 
     @Test
@@ -3389,7 +3395,6 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
                 def originalParsedXml = new 
XmlSlurper().parseText(originalXmlContent)
                 def updatedParsedXml = new 
XmlSlurper().parseText(updatedXmlContent)
                 assert originalParsedXml != updatedParsedXml
-//                assert originalParsedXml.'**'.findAll { it.@encryption } != 
updatedParsedXml.'**'.findAll { it.@encryption }
 
                 def encryptedValues = updatedParsedXml.userGroupProvider.find {
                     it.identifier == 'ldap-user-group-provider'
@@ -3806,19 +3811,21 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
                 // Verify the flow definition
                 def verifyTool = new ConfigEncryptionTool()
                 verifyTool.isVerbose = true
-                verifyTool.flowXmlPath = workingFlowXmlFile.path
-                String updatedFlowXmlContent = verifyTool.loadFlowXml()
+                InputStream updatedFlowXmlContent = 
verifyTool.loadFlowXml(workingFlowXmlFile.path)
 
                 // Check that the flow.xml.gz content changed
                 assert updatedFlowXmlContent != ORIGINAL_FLOW_XML_CONTENT
 
                 // Verify that the cipher texts decrypt correctly
                 logger.info("Original flow.xml.gz cipher texts: 
${originalFlowCipherTexts}")
-                def flowCipherTexts = updatedFlowXmlContent.findAll(WFXCTR)
-                logger.info("Updated  flow.xml.gz cipher texts: 
${flowCipherTexts}")
-                assert flowCipherTexts.size() == CIPHER_TEXT_COUNT
-                flowCipherTexts.every {
-                    assert ConfigEncryptionTool.decryptFlowElement(it, 
newFlowPassword) == "thisIsABadPassword"
+
+                def updatedFlowCipherTexts = 
findFieldsInStream(updatedFlowXmlContent, WFXCTR)
+                logger.info("Updated  flow.xml.gz cipher texts: 
${updatedFlowCipherTexts}")
+                assert updatedFlowCipherTexts.size() == CIPHER_TEXT_COUNT
+                updatedFlowCipherTexts.each {
+                    String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+                    logger.info("Decrypted value of migrated 
${workingFlowXmlFile.path} was: ${decryptedValue}")
+                    assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
                 }
             }
         })
@@ -3911,19 +3918,20 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
                 // Verify the flow definition
                 def verifyTool = new ConfigEncryptionTool()
                 verifyTool.isVerbose = true
-                verifyTool.flowXmlPath = workingFlowXmlFile.path
-                String updatedFlowXmlContent = verifyTool.loadFlowXml()
+                InputStream migratedFlowXmlContent = 
verifyTool.loadFlowXml(workingFlowXmlFile.path)
 
                 // Check that the flow.xml.gz cipher texts did change (new 
salt)
-                assert updatedFlowXmlContent != ORIGINAL_FLOW_XML_CONTENT
+                assert migratedFlowXmlContent != ORIGINAL_FLOW_XML_CONTENT
 
                 // Verify that the cipher texts decrypt correctly
                 logger.info("Original flow.xml.gz cipher texts: 
${originalFlowCipherTexts}")
-                def flowCipherTexts = updatedFlowXmlContent.findAll(WFXCTR)
-                logger.info("Updated  flow.xml.gz cipher texts: 
${flowCipherTexts}")
-                assert flowCipherTexts.size() == CIPHER_TEXT_COUNT
-                flowCipherTexts.every {
-                    assert ConfigEncryptionTool.decryptFlowElement(it, 
newFlowPassword) == "thisIsABadPassword"
+                def migratedFlowCipherTexts = 
findFieldsInStream(migratedFlowXmlContent, WFXCTR)
+                logger.info("Updated  flow.xml.gz cipher texts: 
${migratedFlowCipherTexts}")
+                assert migratedFlowCipherTexts.size() == CIPHER_TEXT_COUNT
+                migratedFlowCipherTexts.each {
+                    String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+                    logger.info("Decrypted value of migrated 
${workingFlowXmlFile.path} was: ${decryptedValue}")
+                    assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
                 }
             }
         })
@@ -3969,7 +3977,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         Files.copy(niFiPropertiesFile.toPath(), 
workingNiFiPropertiesFile.toPath())
 
         // Use a flow definition that was encrypted with the hard-coded 
default SP key
-        File flowXmlFile = new 
File("src/test/resources/flow_default_key.xml.gz")
+        File flowXmlFile = new File("src/test/resources/flow.xml.gz")
         File workingFlowXmlFile = new File("target/tmp/tmp-flow.xml.gz")
         workingFlowXmlFile.delete()
         Files.copy(flowXmlFile.toPath(), workingFlowXmlFile.toPath())
@@ -4053,18 +4061,154 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
                 def verifyTool = new ConfigEncryptionTool()
                 verifyTool.isVerbose = true
                 verifyTool.flowXmlPath = workingFlowXmlFile.path
-                String updatedFlowXmlContent = verifyTool.loadFlowXml()
+                InputStream updatedFlowXmlContent = 
verifyTool.loadFlowXml(workingFlowXmlFile.path)
 
-                // Check that the flow.xml.gz content changed
-                assert updatedFlowXmlContent != ORIGINAL_FLOW_XML_CONTENT
+                def migratedFlowCipherTexts = 
findFieldsInStream(updatedFlowXmlContent, WFXCTR)
 
                 // Verify that the cipher texts decrypt correctly
                 logger.info("Original flow.xml.gz cipher texts: 
${originalFlowCipherTexts}")
-                def flowCipherTexts = updatedFlowXmlContent.findAll(WFXCTR)
-                logger.info("Updated  flow.xml.gz cipher texts: 
${flowCipherTexts}")
-                assert flowCipherTexts.size() == CIPHER_TEXT_COUNT
-                flowCipherTexts.every {
-                    assert ConfigEncryptionTool.decryptFlowElement(it, 
newFlowPassword) == "thisIsABadPassword"
+                logger.info("Updated  flow.xml.gz cipher texts: 
${migratedFlowCipherTexts}")
+                assert migratedFlowCipherTexts.size() == CIPHER_TEXT_COUNT
+                migratedFlowCipherTexts.each {
+                    String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+                    logger.info("Decrypted value of migrated 
${workingFlowXmlFile.path} was: ${decryptedValue}")
+                    assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
+                }
+            }
+        })
+
+        // Act
+        ConfigEncryptionTool.main(args)
+        logger.info("Invoked #main with ${args.join(" ")}")
+    }
+
+    /**
+     * In this scenario, the nifi.properties file has a sensitive key value 
which is already encrypted. The goal is to provide a new provide a new 
sensitive key value, perform the migration of the flow.xml.gz, and update 
nifi.properties with a new encrypted sensitive key value without modifying any 
other nifi.properties values.
+     */
+    @Test
+    void 
testShouldPerformFullOperationOnAFlowXmlWithPreviouslyEncryptedNiFiProperties() 
{
+        // Arrange
+        exit.expectSystemExitWithStatus(0)
+
+        File tmpDir = setupTmpDir()
+
+        File passwordKeyFile = new 
File("src/test/resources/bootstrap_with_root_key_password_128.conf")
+        File bootstrapFile = new File("target/tmp/tmp_bootstrap.conf")
+        bootstrapFile.delete()
+
+        Files.copy(passwordKeyFile.toPath(), bootstrapFile.toPath())
+        final List<String> originalBootstrapLines = bootstrapFile.readLines()
+        String originalKeyLine = originalBootstrapLines.find {
+            it.startsWith(ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX)
+        }
+        final String EXPECTED_KEY_LINE = 
ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX + PASSWORD_KEY_HEX_128
+        logger.info("Original key line from bootstrap.conf: 
${originalKeyLine}")
+        assert originalKeyLine == ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX + 
PASSWORD_KEY_HEX_128
+
+        // Not "handling" NFP, so update in place (not source test resource)
+        String niFiPropertiesTemplatePath = 
"src/test/resources/nifi_with_few_sensitive_properties_protected_aes_password_128.properties"
+        File niFiPropertiesFile = new File(niFiPropertiesTemplatePath)
+
+        File workingNiFiPropertiesFile = new 
File("target/tmp/tmp-nifi.properties")
+        workingNiFiPropertiesFile.delete()
+        Files.copy(niFiPropertiesFile.toPath(), 
workingNiFiPropertiesFile.toPath())
+
+        // Use a flow definition that was encrypted with the hard-coded 
default SP key
+        File flowXmlFile = new File("src/test/resources/flow.xml.gz")
+        File workingFlowXmlFile = new File("target/tmp/tmp-flow.xml.gz")
+        workingFlowXmlFile.delete()
+        Files.copy(flowXmlFile.toPath(), workingFlowXmlFile.toPath())
+
+        // Get the original ciphered fields to compare later
+        def verifyTool = new ConfigEncryptionTool()
+        verifyTool.isVerbose = true
+        def originalFlowCipherTexts = 
findFieldsInStream(verifyTool.loadFlowXml(flowXmlFile.path),  WFXCTR)
+        final int CIPHER_TEXT_COUNT = originalFlowCipherTexts.size()
+
+        // Load both the encrypted and decrypted properties to compare later
+        NiFiPropertiesLoader niFiPropertiesLoader = 
NiFiPropertiesLoader.withKey(PASSWORD_KEY_HEX_128)
+        NiFiProperties inputProperties = 
niFiPropertiesLoader.load(workingNiFiPropertiesFile)
+        logger.info("Loaded ${inputProperties.size()} properties from input 
file")
+        ProtectedNiFiProperties protectedInputProperties = new 
ProtectedNiFiProperties(inputProperties)
+        def originalSensitiveValues = 
protectedInputProperties.getSensitivePropertyKeys().collectEntries { String key 
-> [(key): protectedInputProperties.getProperty(key)] }
+        logger.info("Original sensitive values: ${originalSensitiveValues}")
+
+
+        final String SENSITIVE_PROTECTION_KEY = 
ProtectedNiFiProperties.getProtectionKey(NiFiProperties.SENSITIVE_PROPS_KEY)
+        ProtectedNiFiProperties encryptedProperties = 
niFiPropertiesLoader.readProtectedPropertiesFromDisk(workingNiFiPropertiesFile)
+        def originalEncryptedValues = 
encryptedProperties.getSensitivePropertyKeys().collectEntries { String key -> 
[(key): encryptedProperties.getProperty(key)] }
+        logger.info("Original encrypted values: ${originalEncryptedValues}")
+        String originalSensitiveKeyProtectionScheme = 
encryptedProperties.getProperty(SENSITIVE_PROTECTION_KEY)
+        logger.info("Sensitive property key originally protected with 
${originalSensitiveKeyProtectionScheme}")
+
+        String newFlowPassword = FLOW_PASSWORD
+
+        // Bootstrap path must be provided to decrypt nifi.properties to get 
SP key
+        String[] args = ["-n", workingNiFiPropertiesFile.path, "-f", 
workingFlowXmlFile.path, "-b", bootstrapFile.path, "-x", "-v", "-s", 
newFlowPassword]
+
+        exit.checkAssertionAfterwards(new Assertion() {
+            void checkAssertion() {
+                final List<String> updatedPropertiesLines = 
workingNiFiPropertiesFile.readLines()
+                logger.info("Updated nifi.properties:")
+                logger.info("\n" * 2 + updatedPropertiesLines.join("\n"))
+
+                AESSensitivePropertyProvider spp = new 
AESSensitivePropertyProvider(PASSWORD_KEY_HEX_128)
+
+                // Check that the output values for everything is the same 
except the sensitive props key
+                NiFiProperties updatedProperties = new 
NiFiPropertiesLoader().readProtectedPropertiesFromDisk(workingNiFiPropertiesFile)
+                assert updatedProperties.size() == inputProperties.size()
+                String newSensitivePropertyKey = 
updatedProperties.getProperty(NiFiProperties.SENSITIVE_PROPS_KEY)
+
+                // Check that the encrypted value changed
+                assert newSensitivePropertyKey != 
originalSensitiveValues.get(NiFiProperties.SENSITIVE_PROPS_KEY)
+
+                // Check that the decrypted value is the new password
+                assert spp.unprotect(newSensitivePropertyKey) == 
newFlowPassword
+
+                // Check that all other values stayed the same
+                originalEncryptedValues.every { String key, String 
originalValue ->
+                    if (key != NiFiProperties.SENSITIVE_PROPS_KEY) {
+                        assert updatedProperties.getProperty(key) == 
originalValue
+                    }
+                }
+
+                // Check that all other (decrypted) values stayed the same
+                originalSensitiveValues.every { String key, String 
originalValue ->
+                    if (key != NiFiProperties.SENSITIVE_PROPS_KEY) {
+                        assert 
spp.unprotect(updatedProperties.getProperty(key)) == originalValue
+                    }
+                }
+
+                // Check that the protection scheme did not change
+                String sensitiveKeyProtectionScheme = 
updatedProperties.getProperty(SENSITIVE_PROTECTION_KEY)
+                logger.info("Sensitive property key currently protected with 
${sensitiveKeyProtectionScheme}")
+                assert sensitiveKeyProtectionScheme == 
originalSensitiveKeyProtectionScheme
+
+                // Check that bootstrap.conf did not change
+                final List<String> updatedBootstrapLines = 
bootstrapFile.readLines()
+                String updatedKeyLine = updatedBootstrapLines.find {
+                    it.startsWith(ConfigEncryptionTool.BOOTSTRAP_KEY_PREFIX)
+                }
+                logger.info("Updated key line: ${updatedKeyLine}")
+
+                assert updatedKeyLine == EXPECTED_KEY_LINE
+                assert originalBootstrapLines.size() == 
updatedBootstrapLines.size()
+
+                // Verify the flow definition
+                verifyTool = new ConfigEncryptionTool()
+                verifyTool.isVerbose = true
+                InputStream migratedFlowXmlContent = 
verifyTool.loadFlowXml(workingFlowXmlFile.path)
+
+                def migratedFlowCipherTexts = 
findFieldsInStream(migratedFlowXmlContent, WFXCTR)
+                logger.info("Migrated flow cipher texts for: " + 
workingFlowXmlFile.path)
+                // Verify that the cipher texts decrypt correctly
+                logger.info("Original " + workingFlowXmlFile.path + " unique 
cipher texts: ${originalFlowCipherTexts}")
+                logger.info("Migrated " + workingFlowXmlFile.path + " unique 
cipher texts: ${migratedFlowCipherTexts}")
+                assert migratedFlowCipherTexts.size() == CIPHER_TEXT_COUNT
+                migratedFlowCipherTexts.each {
+                    String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+                    logger.info("Decrypted value of migrated 
${workingFlowXmlFile.path} was: ${decryptedValue}")
+                    assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
                 }
             }
         })
@@ -4118,7 +4262,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         // Read the uncompressed version to compare later
         File originalFlowXmlFile = new 
File("src/test/resources/flow_default_key.xml")
         final String ORIGINAL_FLOW_XML_CONTENT = originalFlowXmlFile.text
-        def originalFlowCipherTexts = ORIGINAL_FLOW_XML_CONTENT.findAll(WFXCTR)
+        def originalFlowCipherTexts = 
ORIGINAL_FLOW_XML_CONTENT.findAll(WFXCTR).toSet()
         final int CIPHER_TEXT_COUNT = originalFlowCipherTexts.size()
 
         // Load both the encrypted and decrypted properties to compare later
@@ -4209,19 +4353,20 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
                 // Verify the flow definition
                 def verifyTool = new ConfigEncryptionTool()
                 verifyTool.isVerbose = true
-                verifyTool.flowXmlPath = workingFlowXmlFile.path
-                String updatedFlowXmlContent = verifyTool.loadFlowXml()
+                InputStream updatedFlowXmlContent = 
verifyTool.loadFlowXml(workingFlowXmlFile.path)
 
                 // Check that the flow.xml.gz content changed
-                assert updatedFlowXmlContent != ORIGINAL_FLOW_XML_CONTENT
+                // TODO assert updatedFlowXmlContent != 
ORIGINAL_FLOW_XML_CONTENT
 
                 // Verify that the cipher texts decrypt correctly
                 logger.info("Original flow.xml.gz cipher texts: 
${originalFlowCipherTexts}")
-                def flowCipherTexts = updatedFlowXmlContent.findAll(WFXCTR)
+                def flowCipherTexts = 
findFieldsInStream(updatedFlowXmlContent, WFXCTR)
                 logger.info("Updated  flow.xml.gz cipher texts: 
${flowCipherTexts}")
                 assert flowCipherTexts.size() == CIPHER_TEXT_COUNT
-                flowCipherTexts.every {
-                    assert ConfigEncryptionTool.decryptFlowElement(it, 
newFlowPassword) == "thisIsABadPassword"
+                flowCipherTexts.each {
+                    String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+                    logger.info("Decrypted value of migrated 
${workingFlowXmlFile.path} was: ${decryptedValue}")
+                    assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
                 }
 
                 // Update the "original" flow cipher texts for the next run to 
the current values
@@ -4289,7 +4434,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     @Test
     void testShouldDecryptFlowXmlContent() {
         // Arrange
-        String existingFlowPassword = "flowPassword"
+        String existingFlowPassword = "nififtw!"
         final String DEFAULT_ALGORITHM = "PBEWITHMD5AND256BITAES-CBC-OPENSSL"
         final String DEFAULT_PROVIDER = "BC"
 
@@ -4326,7 +4471,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
 
         final String EXPECTED_PLAINTEXT = "thisIsABadPassword"
 
-        final String ENCRYPTED_VALUE_FROM_FLOW = 
"enc{2032416987A00D9FCD757528D7AE609D7E793CA5F956641DB53E14CDB9BFCD4037B73AC705CD3F5C1C1BDE18B8D7B281}"
+        final String ENCRYPTED_VALUE_FROM_FLOW = 
"enc{5d8c45f04790e73cba72e5e3fbee1145f2e18256c3b33c283e17f5281611cb5e5f9e6cc988c5be0e8cca7b5dc8fa7cf7}"
 
         // Act
         String decryptedElement = 
ConfigEncryptionTool.decryptFlowElement(ENCRYPTED_VALUE_FROM_FLOW, 
existingFlowPassword, DEFAULT_ALGORITHM, DEFAULT_PROVIDER)
@@ -4339,8 +4484,8 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     @Test
     void testShouldEncryptFlowXmlContent() {
         // Arrange
-        String flowPassword = "flowPassword"
-        String sensitivePropertyValue = "thisIsABadProcessorPassword"
+        String flowPassword = "nififtw!"
+        String sensitivePropertyValue = "thisIsAnotherBadPassword"
         byte[] saltBytes = "thisIsABadSalt..".bytes
 
         StringEncryptor sanityEncryptor = new 
StringEncryptor(DEFAULT_ALGORITHM, DEFAULT_PROVIDER, flowPassword)
@@ -4405,34 +4550,40 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         Files.copy(flowXmlFile.toPath(), workingFile.toPath())
         ConfigEncryptionTool tool = new ConfigEncryptionTool()
         tool.isVerbose = true
+        tool.flowXmlPath = workingFile.path
+        tool.outputFlowXmlPath = workingFile.path
 
         final String SENSITIVE_VALUE = "thisIsABadPassword"
 
         String existingFlowPassword = DEFAULT_LEGACY_SENSITIVE_PROPS_KEY
         String newFlowPassword = FLOW_PASSWORD
 
-        String xmlContent = workingFile.text
-        logger.info("Read flow.xml: \n${xmlContent}")
+        InputStream xmlContent = new FileInputStream(workingFile.path)
+        logger.info("Read flow.xml as input stream.")
 
         // There are two encrypted passwords in this flow
-        int cipherTextCount = xmlContent.findAll(WFXCTR).size()
-        logger.info("Found ${cipherTextCount} encrypted properties in the 
original flow.xml content")
+        int cipherTextCount = findFieldsInStream(xmlContent, WFXCTR).size()
+        logger.info("Found ${cipherTextCount} unique encrypted properties in 
the original flow.xml content")
 
         // Act
-        String migratedXmlContent = tool.migrateFlowXmlContent(xmlContent, 
existingFlowPassword, newFlowPassword)
-        logger.info("Migrated flow.xml: \n${migratedXmlContent}")
+        xmlContent = new FileInputStream(workingFile.path)
+        tool.migrateFlowXmlContent(xmlContent, existingFlowPassword, 
newFlowPassword)
+        logger.info("Migrated flow.xml.")
 
         // Assert
-        def newCipherTexts = migratedXmlContent.findAll(WFXCTR)
-
-        assert newCipherTexts.size() == cipherTextCount
-        newCipherTexts.every {
-            assert ConfigEncryptionTool.decryptFlowElement(it, 
newFlowPassword) == SENSITIVE_VALUE
+        InputStream migratedFlowXmlFile = new FileInputStream(workingFile.path)
+        def migratedCipherTexts = findFieldsInStream(migratedFlowXmlFile, 
WFXCTR)
+
+        assert migratedCipherTexts.size() == cipherTextCount
+        migratedCipherTexts.each {
+            String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+            logger.info("Decrypted value of migrated " + workingFile.path + " 
was: " + decryptedValue)
+            assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
         }
 
         // Ensure that everything else is identical
-        assert migratedXmlContent.replaceAll(WFXCTR, "") ==
-                xmlContent.replaceAll(WFXCTR, "")
+        assert flowXmlFile.text.replaceAll(WFXCTR, "") ==
+                workingFile.text.replaceAll(WFXCTR, "")
     }
 
 
@@ -4449,6 +4600,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         Files.copy(flowXmlFile.toPath(), workingFile.toPath())
         ConfigEncryptionTool tool = new ConfigEncryptionTool()
         tool.isVerbose = true
+        tool.outputFlowXmlPath = workingFile.path
 
         final String SENSITIVE_VALUE = "thisIsABadPassword"
 
@@ -4464,7 +4616,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         final int ORIGINAL_CIPHER_TEXT_COUNT = ORIGINAL_CIPHER_TEXTS.size()
         logger.info("Found ${ORIGINAL_CIPHER_TEXT_COUNT} encrypted properties 
in the original flow.xml content")
 
-        String currentXmlContent = xmlContent
+        InputStream currentXmlContent = new 
ByteArrayInputStream(xmlContent.bytes)
 
         // Act
         passwordProgression.eachWithIndex { String existingFlowPassword, int i 
->
@@ -4472,24 +4624,26 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
                 String newFlowPassword = passwordProgression[i + 1]
                 logger.info("Migrating from ${existingFlowPassword} to 
${newFlowPassword}")
 
-                String migratedXmlContent = 
tool.migrateFlowXmlContent(currentXmlContent, existingFlowPassword, 
newFlowPassword)
+                InputStream migratedXmlContent = 
tool.migrateFlowXmlContent(currentXmlContent, existingFlowPassword, 
newFlowPassword)
 //                logger.info("Migrated flow.xml: \n${migratedXmlContent}")
 
                 // Assert
-                def newCipherTexts = migratedXmlContent.findAll(WFXCTR)
+                def newCipherTexts = findFieldsInStream(migratedXmlContent, 
WFXCTR)
                 logger.info("Cipher texts for iteration ${i}: 
\n${newCipherTexts.join("\n")}")
 
                 assert newCipherTexts.size() == ORIGINAL_CIPHER_TEXT_COUNT
-                newCipherTexts.every {
-                    assert ConfigEncryptionTool.decryptFlowElement(it, 
newFlowPassword) == SENSITIVE_VALUE
+
+                newCipherTexts.each {
+                    String decryptedValue = 
ConfigEncryptionTool.decryptFlowElement(it, newFlowPassword)
+                    assert decryptedValue == PASSWORD || decryptedValue == 
ANOTHER_PASSWORD
                 }
 
                 // Ensure that everything else is identical
-                assert migratedXmlContent.replaceAll(WFXCTR, "") ==
-                        xmlContent.replaceAll(WFXCTR, "")
+                assert new File(workingFile.path).text.replaceAll(WFXCTR, "") 
==
+                        flowXmlFile.text.replaceAll(WFXCTR, "")
 
                 // Update the "source" XML content for the next iteration
-                currentXmlContent = migratedXmlContent
+                currentXmlContent = tool.loadFlowXml(workingFile.path)
             }
         }
     }
@@ -4507,6 +4661,7 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         Files.copy(flowXmlFile.toPath(), workingFile.toPath())
         ConfigEncryptionTool tool = new ConfigEncryptionTool()
         tool.isVerbose = true
+        tool.outputFlowXmlPath = workingFile.path
 
         String existingFlowPassword = DEFAULT_LEGACY_SENSITIVE_PROPS_KEY
         String newFlowPassword = FLOW_PASSWORD
@@ -4519,11 +4674,11 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         logger.info("Found ${cipherTextCount} encrypted properties in the 
original flow.xml content")
 
         // Act
-        String migratedXmlContent = tool.migrateFlowXmlContent(xmlContent, 
existingFlowPassword, newFlowPassword)
-        logger.info("Migrated flow.xml: \n${migratedXmlContent}")
+        InputStream migratedXmlContent = tool.migrateFlowXmlContent(new 
ByteArrayInputStream(xmlContent.bytes), existingFlowPassword, newFlowPassword)
+        logger.info("Migrated flow.xml.")
 
         // Assert
-        def newCipherTexts = migratedXmlContent.findAll(WFXCTR)
+        def newCipherTexts = findFieldsInStream(migratedXmlContent, WFXCTR)
 
         assert newCipherTexts.size() == cipherTextCount
 
@@ -4551,17 +4706,19 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         Files.copy(flowXmlFile.toPath(), workingFile.toPath())
         ConfigEncryptionTool tool = new ConfigEncryptionTool()
         tool.isVerbose = true
+        tool.flowXmlPath = workingFile.path
+        tool.outputFlowXmlPath = workingFile.path
 
         // Use the wrong existing password
         String wrongExistingFlowPassword = 
DEFAULT_LEGACY_SENSITIVE_PROPS_KEY.reverse()
         String newFlowPassword = FLOW_PASSWORD
 
-        String xmlContent = workingFile.text
+        InputStream xmlContent = new ByteArrayInputStream(workingFile.bytes)
 
         // Act
         def message = shouldFail(BadPaddingException) {
-            String migratedXmlContent = tool.migrateFlowXmlContent(xmlContent, 
wrongExistingFlowPassword, newFlowPassword)
-            logger.info("Migrated flow.xml: \n${migratedXmlContent}")
+            InputStream migratedXmlContent = 
tool.migrateFlowXmlContent(xmlContent, wrongExistingFlowPassword, 
newFlowPassword)
+            logger.info("Migrated flow.xml.")
         }
         logger.expected(message)
 
@@ -4575,26 +4732,23 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     @Test
     void 
testHandleFlowXmlMigrationWithIncorrectExistingPasswordShouldProvideHelpfulErrorMessage()
 {
         // Arrange
-//        
exit.expectSystemExitWithStatus(ExitCode.ERROR_MIGRATING_FLOW.ordinal())
         systemOutRule.clearLog()
 
         String flowXmlPath = "src/test/resources/flow.xml"
         File flowXmlFile = new File(flowXmlPath)
 
-        File tmpDir = setupTmpDir()
+        // Use the wrong existing password
+        String wrongExistingFlowPassword = 
DEFAULT_LEGACY_SENSITIVE_PROPS_KEY.reverse()
+        String newFlowPassword = FLOW_PASSWORD
+
+        def nifiProperties = wrapNFP([(NiFiProperties.SENSITIVE_PROPS_KEY): 
wrongExistingFlowPassword])
 
         File workingFile = new File("target/tmp/tmp-flow.xml")
         workingFile.delete()
         Files.copy(flowXmlFile.toPath(), workingFile.toPath())
         ConfigEncryptionTool tool = new ConfigEncryptionTool()
         tool.isVerbose = true
-
-        // Use the wrong existing password
-        String wrongExistingFlowPassword = 
DEFAULT_LEGACY_SENSITIVE_PROPS_KEY.reverse()
-        String newFlowPassword = FLOW_PASSWORD
-
-        tool.flowXml = workingFile.text
-        def nifiProperties = wrapNFP([(NiFiProperties.SENSITIVE_PROPS_KEY): 
wrongExistingFlowPassword])
+        tool.flowXmlInputStream = tool.loadFlowXml(workingFile.path)
         tool.niFiProperties = nifiProperties
         tool.flowPropertiesPassword = newFlowPassword
         tool.handlingNiFiProperties = false
@@ -4602,23 +4756,12 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         // Act
         def message = shouldFail(Exception) {
             tool.handleFlowXml()
-            logger.info("Migrated flow.xml: \n${tool.flowXml}")
+            logger.info("Migrated flow.xml.")
         }
         logger.expected(message)
 
-//        final String standardOutput = systemOutRule.getLog()
-//        List<String> lines = standardOutput.split("\n")
-//        logger.info("Captured ${lines.size()} lines of log output")
-//        lines.each { String l -> logger.info("\t$l") }
-
-//        final String errorOutput = systemErrRule.getLog()
-//        List<String> errorlines = errorOutput.split("\n")
-//        logger.info("Captured ${errorlines.size()} lines of error log 
output")
-//        errorlines.each { String l -> logger.info("\t$l") }
-
         // Assert
         // TODO: Assert that this message was in the log output (neither the 
STDOUT and STDERR buffers contain it, but it is printed)
-//        assert message =~ "Error performing flow XML content migration 
because some sensitive values could not be decrypted. Ensure that the existing 
flow password \\[\\-p\\] is correct."
         assert message == "Encountered an error migrating flow content"
     }
 
@@ -4646,13 +4789,14 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         Files.copy(flowXmlGzFile.toPath(), workingGzFile.toPath())
         ConfigEncryptionTool tool = new ConfigEncryptionTool()
         tool.isVerbose = true
-        tool.flowXmlPath = workingGzFile.path
 
         String xmlContent = workingFile.text
         logger.info("Read flow.xml: \n${xmlContent}")
 
         // Act
-        String readXmlContent = tool.loadFlowXml()
+        InputStream xmlContentStream = tool.loadFlowXml(workingGzFile.path)
+        String readXmlContent = IOUtils.toString(xmlContentStream, 
StandardCharsets.UTF_8)
+        
         logger.info("Loaded flow.xml.gz: \n${readXmlContent}")
 
         // Assert
@@ -4660,40 +4804,6 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
     }
 
     @Test
-    void testShouldWriteFlowXmlToFile() {
-        // Arrange
-        String flowXmlPath = "src/test/resources/flow.xml"
-        File flowXmlFile = new File(flowXmlPath)
-
-        String flowXmlGzPath = "src/test/resources/flow.xml.gz"
-        File flowXmlGzFile = new File(flowXmlGzPath)
-
-        File tmpDir = setupTmpDir()
-
-        File workingFile = new File("target/tmp/tmp-flow.xml")
-        workingFile.delete()
-        Files.copy(flowXmlFile.toPath(), workingFile.toPath())
-        File workingGzFile = new File("target/tmp/tmp-flow.xml.gz")
-        workingGzFile.delete()
-        Files.copy(flowXmlGzFile.toPath(), workingGzFile.toPath())
-        ConfigEncryptionTool tool = new ConfigEncryptionTool()
-        tool.isVerbose = true
-        tool.outputFlowXmlPath = workingGzFile.path.replaceAll("flow.xml.gz", 
"output.xml.gz")
-
-        String xmlContent = workingFile.text
-        logger.info("Read flow.xml: \n${xmlContent}")
-
-        // Act
-        tool.writeFlowXmlToFile(xmlContent)
-
-        // Assert
-
-        // Set the input path to what was just written and rely on the 
separately-tested load method to uncompress and read the contents
-        tool.flowXmlPath = tool.outputFlowXmlPath
-        assert tool.loadFlowXml() == xmlContent
-    }
-
-    @Test
     void testShouldDetectActionFlags() {
         // Arrange
         final def HELP_AND_VERBOSE_ARGS = [["-h", "--help"], ["-v", 
"--verbose"]]
@@ -5159,6 +5269,17 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         }
     }
 
+    @Test
+    void testFindFieldsInStream() {
+        def verifyTool = new ConfigEncryptionTool()
+        verifyTool.isVerbose = true
+        verifyTool.flowXmlPath = new 
File("src/test/resources/flow.xml.gz").path
+        InputStream updatedFlowXmlContent = 
verifyTool.loadFlowXml(verifyTool.flowXmlPath)
+        Set<String> fieldsFound = findFieldsInStream(updatedFlowXmlContent, 
WFXCTR)
+        logger.info("Found " + fieldsFound.size() + " fields in " + 
verifyTool.flowXmlPath + " that matched " + WFXCTR)
+        assert(fieldsFound.size() > 0)
+    }
+
     static boolean compareXMLFragments(String expectedXML, String actualXML) {
         Diff diffSimilar = DiffBuilder.compare(expectedXML).withTest(actualXML)
                 .withNodeMatcher(new 
DefaultNodeMatcher(ElementSelectors.byName))
@@ -5172,6 +5293,19 @@ class ConfigEncryptionToolTest extends GroovyTestCase {
         !diffSimilar.hasDifferences()
     }
 
+    static Set<String> findFieldsInStream(InputStream fileInputStream, String 
pattern) {
+        Set<String> fieldsFound = new HashSet<String>()
+        Reader reader = new BufferedReader(new 
InputStreamReader(fileInputStream))
+        String line
+        while((line = reader.readLine()) != null) {
+            def matcher = line =~ pattern
+            if(matcher.find()) {
+                fieldsFound.add(matcher.getAt(0))
+            }
+        }
+        fieldsFound
+    }
+
 // TODO: Test with 128/256-bit available
 }
 
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml
index f47a883..3e2067b 100644
--- a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml
+++ b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml
@@ -60,7 +60,7 @@
                 </property>
                 <property>
                     <name>Password</name>
-                    
<value>enc{2032416987A00D9FCD757528D7AE609D7E793CA5F956641DB53E14CDB9BFCD4037B73AC705CD3F5C1C1BDE18B8D7B281}</value>
+                    
<value>enc{7468697349734142616453616c742e2e79ad38319f8069990c6ac60cad75639f9d2ab7dff1a0b8b488af2fff659cf353}</value>
                 </property>
                 <property>
                     <name>raw-key-hex</name>
@@ -112,7 +112,7 @@
                 </property>
                 <property>
                     <name>Password</name>
-                    
<value>enc{4B580C55B8355FE57A599B31B3B2ACA77429DBF6887C177417624026469E895F0C89FB0C9D9C64C5B2AD943035689C9C}</value>
+                    
<value>enc{7468697349734142616453616c742e2e63a2cd00e648c459e7e45223fefe8d38ab9ed3d71d94be57d5b0e4391980c858}</value>
                 </property>
                 <property>
                     <name>raw-key-hex</name>
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml.gz 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml.gz
index 5ebf096..5cf5f8c 100644
Binary files 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml.gz and 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow.xml.gz differ
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml
index f47a883..3e2067b 100644
--- 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml
+++ 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml
@@ -60,7 +60,7 @@
                 </property>
                 <property>
                     <name>Password</name>
-                    
<value>enc{2032416987A00D9FCD757528D7AE609D7E793CA5F956641DB53E14CDB9BFCD4037B73AC705CD3F5C1C1BDE18B8D7B281}</value>
+                    
<value>enc{7468697349734142616453616c742e2e79ad38319f8069990c6ac60cad75639f9d2ab7dff1a0b8b488af2fff659cf353}</value>
                 </property>
                 <property>
                     <name>raw-key-hex</name>
@@ -112,7 +112,7 @@
                 </property>
                 <property>
                     <name>Password</name>
-                    
<value>enc{4B580C55B8355FE57A599B31B3B2ACA77429DBF6887C177417624026469E895F0C89FB0C9D9C64C5B2AD943035689C9C}</value>
+                    
<value>enc{7468697349734142616453616c742e2e63a2cd00e648c459e7e45223fefe8d38ab9ed3d71d94be57d5b0e4391980c858}</value>
                 </property>
                 <property>
                     <name>raw-key-hex</name>
diff --git 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml.gz
 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml.gz
index bb7c931..debbf15 100644
Binary files 
a/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml.gz
 and 
b/nifi-toolkit/nifi-toolkit-encrypt-config/src/test/resources/flow_default_key.xml.gz
 differ

Reply via email to