Repository: nifi
Updated Branches:
  refs/heads/master 3c7012ffd -> f6b171d5f


NIFI-5833 This closes #3180. Marked GetTwitter Consumer Key and Access Token 
processor properties as sensitive.
NIFI-5833 Added unit test to demonstrate arbitrary decryption of sensitive 
values regardless of processor property sensitive status.
NIFI-5833 Updated GetTwitter documentation with note about 1.9.0+ marking 
Consumer Key and Access Token as sensitive.

Signed-off-by: joewitt <[email protected]>


Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/f6b171d5
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/f6b171d5
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/f6b171d5

Branch: refs/heads/master
Commit: f6b171d5f76aabf59c1e4644927ab6484be9839a
Parents: 3c7012f
Author: Andy LoPresto <[email protected]>
Authored: Tue Nov 20 18:15:40 2018 -0800
Committer: joewitt <[email protected]>
Committed: Wed Nov 21 10:02:25 2018 -0500

----------------------------------------------------------------------
 .../serialization/FlowFromDOMFactoryTest.groovy | 82 ++++++++++++++++++++
 .../nifi/processors/twitter/GetTwitter.java     | 37 +++++----
 2 files changed, 100 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/f6b171d5/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/serialization/FlowFromDOMFactoryTest.groovy
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/serialization/FlowFromDOMFactoryTest.groovy
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/serialization/FlowFromDOMFactoryTest.groovy
index 4beae84..8f82d3a 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/serialization/FlowFromDOMFactoryTest.groovy
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/groovy/org/apache/nifi/controller/serialization/FlowFromDOMFactoryTest.groovy
@@ -17,12 +17,14 @@
 package org.apache.nifi.controller.serialization
 
 import org.apache.commons.codec.binary.Hex
+import org.apache.nifi.controller.StandardFlowSynchronizer
 import org.apache.nifi.encrypt.EncryptionException
 import org.apache.nifi.encrypt.StringEncryptor
 import org.apache.nifi.properties.StandardNiFiProperties
 import org.apache.nifi.security.kms.CryptoUtils
 import org.apache.nifi.security.util.EncryptionMethod
 import org.apache.nifi.util.NiFiProperties
+import org.apache.nifi.web.api.dto.ProcessGroupDTO
 import org.bouncycastle.jce.provider.BouncyCastleProvider
 import org.junit.After
 import org.junit.Before
@@ -32,12 +34,15 @@ import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import org.slf4j.Logger
 import org.slf4j.LoggerFactory
+import org.w3c.dom.Document
+import org.w3c.dom.Element
 
 import javax.crypto.Cipher
 import javax.crypto.SecretKey
 import javax.crypto.SecretKeyFactory
 import javax.crypto.spec.PBEKeySpec
 import javax.crypto.spec.PBEParameterSpec
+import java.nio.charset.StandardCharsets
 import java.security.Security
 
 import static groovy.test.GroovyAssert.shouldFail
@@ -135,6 +140,83 @@ class FlowFromDOMFactoryTest {
         assert msg.message =~ "Check that the ${KEY} value in nifi.properties 
matches the value used to encrypt the flow.xml.gz file"
     }
 
+    @Test
+    void 
testShouldDecryptSensitiveFlowValueRegardlessOfPropertySensitiveStatus() throws 
Exception {
+        // Arrange
+
+        // Create a mock Element object to be parsed
+
+        // TODO: Mock call to StandardFlowSynchronizer#readFlowFromDisk()
+        final String FLOW_XML = """<?xml version="1.0" encoding="UTF-8" 
standalone="no"?>
+<flowController encoding-version="1.3">
+  <maxTimerDrivenThreadCount>10</maxTimerDrivenThreadCount>
+  <maxEventDrivenThreadCount>5</maxEventDrivenThreadCount>
+  <registries/>
+  <rootGroup>
+    <id>32aeba59-0167-1000-fc76-847bf5d10d73</id>
+    <name>NiFi Flow</name>
+    <position x="0.0" y="0.0"/>
+    <comment/>
+    <processor>
+      <id>32af5e4e-0167-1000-ad5f-c79ff57c851e</id>
+      <name>Example Processor</name>
+      <position x="461.0" y="80.0"/>
+      <styles/>
+      <comment/>
+      <class>org.apache.nifi.processors.test.ExampleProcessor</class>
+      <bundle>
+        <group>org.apache.nifi</group>
+        <artifact>nifi-test-nar</artifact>
+        <version>1.9.0-SNAPSHOT</version>
+      </bundle>
+      <maxConcurrentTasks>1</maxConcurrentTasks>
+      <schedulingPeriod>0 sec</schedulingPeriod>
+      <penalizationPeriod>30 sec</penalizationPeriod>
+      <yieldPeriod>1 sec</yieldPeriod>
+      <bulletinLevel>WARN</bulletinLevel>
+      <lossTolerant>false</lossTolerant>
+      <scheduledState>STOPPED</scheduledState>
+      <schedulingStrategy>TIMER_DRIVEN</schedulingStrategy>
+      <executionNode>ALL</executionNode>
+      <runDurationNanos>0</runDurationNanos>
+      <property>
+        <name>Plaintext Property</name>
+        <value>plain value</value>
+      </property>
+      <property>
+        <name>Sensitive Property</name>
+        
<value>enc{29077eedc9af7515cc3e0d2d29a93a5cbb059164876948458fd0c890009c8661}</value>
+      </property>
+    </processor>
+  </rootGroup>
+  <controllerServices/>
+  <reportingTasks/>
+</flowController>
+"""
+
+        // TODO: Mock call to StandardFlowSynchronizer#parseFlowBytes()
+        Document flow = 
StandardFlowSynchronizer.parseFlowBytes(FLOW_XML.getBytes(StandardCharsets.UTF_8))
+
+        // Logic to extract root process group
+        final Element rootElement = flow.getDocumentElement()
+
+        final Element rootGroupElement = (Element) 
rootElement.getElementsByTagName("rootGroup").item(0)
+        final FlowEncodingVersion encodingVersion = 
FlowEncodingVersion.parse(rootGroupElement)
+
+        StringEncryptor flowEncryptor = new 
StringEncryptor(EncryptionMethod.MD5_128AES.algorithm, 
EncryptionMethod.MD5_128AES.provider, DEFAULT_PASSWORD)
+
+        // Act
+        ProcessGroupDTO decryptedProcessGroupDTO = 
FlowFromDOMFactory.getProcessGroup(null, rootGroupElement, flowEncryptor, 
encodingVersion)
+        logger.info("PG DTO: ${decryptedProcessGroupDTO}")
+
+        // Assert
+        def processorProperties = 
decryptedProcessGroupDTO.contents.processors.first().config.properties
+        logger.info("Parsed processor properties: ${processorProperties}")
+
+        assert processorProperties.find { it.key == "Plaintext Property" 
}.value == "plain value"
+        assert processorProperties.find { it.key == "Sensitive Property" 
}.value == "sensitive value"
+    }
+
     private
     static Cipher generateCipher(boolean encryptMode, String password = 
DEFAULT_PASSWORD, byte[] salt = DEFAULT_SALT, int iterationCount = 
DEFAULT_ITERATION_COUNT) {
         // Initialize secret key from password

http://git-wip-us.apache.org/repos/asf/nifi/blob/f6b171d5/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
 
b/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
index e099449..f0a8b0c 100644
--- 
a/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
+++ 
b/nifi-nar-bundles/nifi-social-media-bundle/nifi-twitter-processors/src/main/java/org/apache/nifi/processors/twitter/GetTwitter.java
@@ -16,9 +16,21 @@
  */
 package org.apache.nifi.processors.twitter;
 
+import com.twitter.hbc.ClientBuilder;
+import com.twitter.hbc.core.Client;
+import com.twitter.hbc.core.Constants;
+import com.twitter.hbc.core.endpoint.Location;
+import com.twitter.hbc.core.endpoint.Location.Coordinate;
+import com.twitter.hbc.core.endpoint.StatusesFilterEndpoint;
+import com.twitter.hbc.core.endpoint.StatusesFirehoseEndpoint;
+import com.twitter.hbc.core.endpoint.StatusesSampleEndpoint;
+import com.twitter.hbc.core.endpoint.StreamingEndpoint;
+import com.twitter.hbc.core.event.Event;
+import com.twitter.hbc.core.processor.StringDelimitedProcessor;
+import com.twitter.hbc.httpclient.auth.Authentication;
+import com.twitter.hbc.httpclient.auth.OAuth1;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.net.MalformedURLException;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,7 +43,6 @@ import java.util.Set;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.regex.Pattern;
-
 import org.apache.nifi.annotation.behavior.InputRequirement;
 import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
 import org.apache.nifi.annotation.behavior.SupportsBatching;
@@ -56,24 +67,10 @@ import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processor.io.OutputStreamCallback;
 import org.apache.nifi.processor.util.StandardValidators;
 
-import com.twitter.hbc.ClientBuilder;
-import com.twitter.hbc.core.Client;
-import com.twitter.hbc.core.Constants;
-import com.twitter.hbc.core.endpoint.Location ;
-import com.twitter.hbc.core.endpoint.Location.Coordinate ;
-import com.twitter.hbc.core.endpoint.StatusesFilterEndpoint;
-import com.twitter.hbc.core.endpoint.StatusesFirehoseEndpoint;
-import com.twitter.hbc.core.endpoint.StatusesSampleEndpoint;
-import com.twitter.hbc.core.endpoint.StreamingEndpoint;
-import com.twitter.hbc.core.event.Event;
-import com.twitter.hbc.core.processor.StringDelimitedProcessor;
-import com.twitter.hbc.httpclient.auth.Authentication;
-import com.twitter.hbc.httpclient.auth.OAuth1;
-
 @SupportsBatching
 @InputRequirement(Requirement.INPUT_FORBIDDEN)
 @Tags({"twitter", "tweets", "social media", "status", "json"})
-@CapabilityDescription("Pulls status changes from Twitter's streaming API")
+@CapabilityDescription("Pulls status changes from Twitter's streaming API. In 
versions starting with 1.9.0, the Consumer Key and Access Token are marked as 
sensitive according to 
https://developer.twitter.com/en/docs/basics/authentication/guides/securing-keys-and-tokens";)
 @WritesAttribute(attribute = "mime.type", description = "Sets mime type to 
application/json")
 public class GetTwitter extends AbstractProcessor {
 
@@ -92,6 +89,7 @@ public class GetTwitter extends AbstractProcessor {
             .name("Consumer Key")
             .description("The Consumer Key provided by Twitter")
             .required(true)
+            .sensitive(true)
             .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
             .build();
     public static final PropertyDescriptor CONSUMER_SECRET = new 
PropertyDescriptor.Builder()
@@ -105,6 +103,7 @@ public class GetTwitter extends AbstractProcessor {
             .name("Access Token")
             .description("The Access Token provided by Twitter")
             .required(true)
+            .sensitive(true)
             .addValidator(StandardValidators.NON_EMPTY_VALIDATOR)
             .build();
     public static final PropertyDescriptor ACCESS_TOKEN_SECRET = new 
PropertyDescriptor.Builder()
@@ -221,7 +220,7 @@ public class GetTwitter extends AbstractProcessor {
     }
 
     @OnScheduled
-    public void onScheduled(final ProcessContext context) throws 
MalformedURLException {
+    public void onScheduled(final ProcessContext context) {
         final String endpointName = context.getProperty(ENDPOINT).getValue();
         final Authentication oauth = new 
OAuth1(context.getProperty(CONSUMER_KEY).getValue(),
                 context.getProperty(CONSUMER_SECRET).getValue(),
@@ -369,7 +368,7 @@ public class GetTwitter extends AbstractProcessor {
         flowFile = session.putAllAttributes(flowFile, attributes);
 
         session.transfer(flowFile, REL_SUCCESS);
-        session.getProvenanceReporter().receive(flowFile, 
Constants.STREAM_HOST + client.getEndpoint().getURI().toString());
+        session.getProvenanceReporter().receive(flowFile, 
Constants.STREAM_HOST + client.getEndpoint().getURI());
     }
 
     private static class FollowingValidator implements Validator {

Reply via email to