Github user kevdoran commented on a diff in the pull request:

    https://github.com/apache/nifi/pull/2376#discussion_r159976150
  
    --- Diff: 
nifi-toolkit/nifi-toolkit-encrypt-config/src/main/groovy/org/apache/nifi/toolkit/encryptconfig/DecryptMode.groovy
 ---
    @@ -0,0 +1,322 @@
    +/*
    + * 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.toolkit.encryptconfig
    +
    +import org.apache.commons.cli.HelpFormatter
    +import org.apache.nifi.properties.AESSensitivePropertyProvider
    +import org.apache.nifi.properties.SensitivePropertyProvider
    +import org.apache.nifi.toolkit.encryptconfig.util.BootstrapUtil
    +import org.apache.nifi.toolkit.encryptconfig.util.PropertiesEncryptor
    +import org.apache.nifi.toolkit.encryptconfig.util.ToolUtilities
    +import org.apache.nifi.toolkit.encryptconfig.util.XmlEncryptor
    +import org.apache.nifi.util.console.TextDevices
    +import org.slf4j.Logger
    +import org.slf4j.LoggerFactory
    +
    +class DecryptMode implements ToolMode {
    +
    +    private static final Logger logger = 
LoggerFactory.getLogger(DecryptMode.class)
    +
    +    enum FileType {
    +        properties,
    +        xml
    +    }
    +
    +    CliBuilder cli
    +
    +    DecryptMode() {
    +        cli = cliBuilder()
    +    }
    +
    +    void printUsage(String message = "") {
    +        if (message) {
    +            System.out.println(message)
    +            System.out.println()
    +        }
    +        cli.usage()
    +    }
    +
    +    void printUsageAndExit(String message = "", int exitStatusCode) {
    +        printUsage(message)
    +        System.exit(exitStatusCode)
    +    }
    +
    +    @Override
    +    void run(String[] args) {
    +        logger.warn("The decryption capability of this tool is still 
considered experimental. The results should be manually verified.")
    +        try {
    +
    +            def options = cli.parse(args)
    +
    +            if (!options || options.h) {
    +                printUsageAndExit("", EncryptConfigMain.EXIT_STATUS_OTHER)
    +            }
    +
    +            EncryptConfigLogger.configureLogger(options.v)
    +
    +            DecryptConfiguration config = new DecryptConfiguration(options)
    +
    +            run(config)
    +
    +        } catch (Exception e) {
    +            logger.error("Encountered an error: ${e.getMessage()}")
    +            logger.debug("", e) // stack trace only when verbose enabled
    +            printUsageAndExit(e.getMessage(), 
EncryptConfigMain.EXIT_STATUS_FAILURE)
    +        }
    +    }
    +
    +    void run(DecryptConfiguration config) throws Exception {
    +
    +        if (!config.fileType) {
    +
    +            // Try to load the input file to auto-detect the file type
    +            boolean isPropertiesFile = 
PropertiesEncryptor.supportsFile(config.inputFilePath)
    +
    +            boolean isXmlFile = 
XmlEncryptor.supportsFile(config.inputFilePath)
    +
    +            if (ToolUtilities.isExactlyOneTrue(isPropertiesFile, 
isXmlFile)) {
    +                if (isPropertiesFile) {
    +                    config.fileType = FileType.properties
    +                    logger.debug("Auto-detection of input file type 
determined the type to be: ${FileType.properties}")
    +                }
    +                if (isXmlFile) {
    +                    config.fileType = FileType.xml
    +                    logger.debug("Auto-detection of input file type 
determined the type to be: ${FileType.xml}")
    +                }
    +            }
    +
    +            // Could we successfully auto-detect?
    +            if (!config.fileType) {
    +                throw new RuntimeException("Auto-detection of input file 
type failed. Please re-run the tool specifying the file type with the 
-t/--fileType flag.")
    +            }
    +        }
    +
    +        String decryptedSerializedContent = null
    +        switch (config.fileType) {
    +
    +            case FileType.properties:
    +                PropertiesEncryptor propertiesEncryptor = new 
PropertiesEncryptor(null, config.decryptionProvider)
    +                Properties properties = 
propertiesEncryptor.loadFile(config.inputFilePath)
    +                properties = propertiesEncryptor.decrypt(properties)
    +                decryptedSerializedContent = 
propertiesEncryptor.serializePropertiesAndPreserveFormatIfPossible(properties, 
config.inputFilePath)
    +                break
    +
    +            case FileType.xml:
    +                XmlEncryptor xmlEncryptor = new XmlEncryptor(null, 
config.decryptionProvider) {
    +                    @Override
    +                    List<String> 
serializeXmlContentAndPreserveFormat(String updatedXmlContent, File 
originalInputXmlFile) {
    +                        // For decrypting unknown, generic XML, this tool 
will not support preserving the format
    +                        return updatedXmlContent.split("\n")
    +                    }
    +                }
    +
    +                String xmlContent = 
xmlEncryptor.loadXmlFile(config.inputFilePath)
    +                xmlContent = xmlEncryptor.decrypt(xmlContent)
    +                decryptedSerializedContent = 
xmlEncryptor.serializeXmlContentAndPreserveFormatIfPossible(xmlContent, 
config.inputFilePath)
    +                break
    +
    +            default:
    +                throw new RuntimeException("Unsupported file type 
'${config.fileType}'")
    +        }
    +
    +        if (!decryptedSerializedContent) {
    +            throw new RuntimeException("Failed to load and decrypt input 
file.")
    +        }
    +
    +        if (config.outputToFile) {
    +            try {
    +                File outputFile = new File(config.outputFilePath)
    +                if (ToolUtilities.isSafeToWrite(outputFile)) {
    +                    outputFile.text = decryptedSerializedContent
    +                    logger.info("Wrote decrypted file contents to 
'${config.outputFilePath}'")
    +                }
    +            } catch (IOException e) {
    +                throw new RuntimeException("Encountered an exception 
writing the decrypted content to '${config.outputFilePath}': 
${e.getMessage()}", e)
    +            }
    +        } else {
    +            System.out.println(decryptedSerializedContent)
    +        }
    +
    +    }
    +
    +    private CliBuilder cliBuilder() {
    +
    +        String usage = "${EncryptConfigMain.class.getCanonicalName()} 
decrypt [options] file"
    +
    +        int formatWidth = EncryptConfigMain.HELP_FORMAT_WIDTH
    +        HelpFormatter formatter = new HelpFormatter()
    +        formatter.setWidth(formatWidth)
    +        formatter.setOptionComparator(null) // preserve order of options 
below in help text
    +
    +        CliBuilder cli = new CliBuilder(
    +                usage: usage,
    +                width: formatWidth,
    +                formatter: formatter,
    +                stopAtNonOption: false)
    +
    +        cli.h(longOpt: 'help', 'Show usage information (this message)')
    +        cli.v(longOpt: 'verbose', 'Enables verbose mode (off by default)')
    +
    +        // Options for the password or key or bootstrap.conf
    +        cli.p(longOpt: 'password',
    +                args: 1,
    +                argName: 'password',
    +                optionalArg: true,
    +                'Use a password to derive the key to decrypt the input 
file. If an argument is not provided to this flag, interactive mode will be 
triggered to prompt the user to enter the password.')
    +        cli.k(longOpt: 'key',
    +                args: 1,
    +                argName: 'keyhex',
    +                optionalArg: true,
    +                'Use a raw hexadecimal key to decrypt the input file. If 
an argument is not provided to this flag, interactive mode will be triggered to 
prompt the user to enter the key.')
    +        cli.b(longOpt: 'bootstrapConf',
    +                args: 1,
    +                argName: 'file',
    +                'Use a bootstrap.conf file containing the master key to 
decrypt the input file (as an alternative to -p or -k)')
    +
    +        cli.o(longOpt: 'output',
    +                args: 1,
    +                argName: 'file',
    +                'Specify an output file. If omitted, Standard Out is used. 
Output file can be set to the input file to decrypt the file in-place.')
    +
    +        return cli
    +
    +    }
    +
    +    static class DecryptConfiguration {
    --- End diff --
    
    Yes.
    - [ ] Adopt the Configuration interface where applicable to convey intended 
class structure to future developers as the Configuration interface is filled 
in more.


---

Reply via email to