You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@nifi.apache.org by GitBox <gi...@apache.org> on 2020/08/27 14:46:45 UTC

[GitHub] [nifi] dml872 opened a new pull request #4495: NIFI-7475 JWT processor bundle

dml872 opened a new pull request #4495:
URL: https://github.com/apache/nifi/pull/4495


   Thank you for submitting a contribution to Apache NiFi.
   
   Please provide a short description of the PR here:
   
   #### Description of PR
   
   NIFI-7475 Processor bundle to JWT sign/unsign JSON
   
   In order to streamline the review of the contribution we ask you
   to ensure the following steps have been taken:
   
   ### For all changes:
   - [ ] Is there a JIRA ticket associated with this PR? Is it referenced 
        in the commit message?
   
   - [ ] Does your PR title start with **NIFI-XXXX** where XXXX is the JIRA number you are trying to resolve? Pay particular attention to the hyphen "-" character.
   
   - [ ] Has your PR been rebased against the latest commit within the target branch (typically `main`)?
   
   - [ ] Is your initial contribution a single, squashed commit? _Additional commits in response to PR reviewer feedback should be made on this branch and pushed to allow change tracking. Do not `squash` or use `--force` when pushing to allow for clean monitoring of changes._
   
   ### For code changes:
   - [ ] Have you ensured that the full suite of tests is executed via `mvn -Pcontrib-check clean install` at the root `nifi` folder?
   - [ ] Have you written or updated unit tests to verify your changes?
   - [ ] Have you verified that the full build is successful on JDK 8?
   - [ ] Have you verified that the full build is successful on JDK 11?
   - [ ] If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under [ASF 2.0](http://www.apache.org/legal/resolved.html#category-a)? 
   - [ ] If applicable, have you updated the `LICENSE` file, including the main `LICENSE` file under `nifi-assembly`?
   - [ ] If applicable, have you updated the `NOTICE` file, including the main `NOTICE` file found under `nifi-assembly`?
   - [ ] If adding new Properties, have you added `.displayName` in addition to .name (programmatic access) for each of the new properties?
   
   ### For documentation related changes:
   - [ ] Have you ensured that format looks appropriate for the output in which it is rendered?
   
   ### Note:
   Please ensure that once the PR is submitted, you check GitHub Actions CI for build issues and submit an update to your PR as soon as possible.
   


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482594226



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")
+            .displayName("Approved Public Keys")
+            .description("Path to the directory containing approved public certificates. Certificates are expected to have "
+                + "extension \".pub\" and be x509 PEM files")
+            .required(true).addValidator(CustomValidators.DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR).build();
+
+    public static final PropertyDescriptor JWT_ATTRIBUTE_NAME = new PropertyDescriptor.Builder().name("JWT_ATTRIBUTE_NAME")
+            .displayName("JWT Attribute name")
+            .description("The name of the attribute which contains the JWT. Leave empty to read JWT from the flowfile content")
+            .required(false).defaultValue("").addValidator(StandardValidators.ATTRIBUTE_KEY_PROPERTY_NAME_VALIDATOR).build();
+
+    public static final Relationship SUCCESS_REL = new Relationship.Builder().name("success")
+            .description("A FlowFile is routed to this relationship after its JWT has been validated and unsigned")
+            .build();
+
+    public static final Relationship FAILURE_REL = new Relationship.Builder().name("failure").description(
+            "A FlowFile is routed to this relationship if the JWT cannot be validated or unsigned for any reason")
+            .build();
+
+    public static final String HEADER_ATTRIBUTE = "jwt.header";
+    public static final String FOOTER_ATTRIBUTE = "jwt.footer";
+    public static final String JWT_PREFIX_ATTRIBUTE = "jwt.";
+
+    public static final String PUBLIC_KEY_PREFIX = "-----BEGIN PUBLIC KEY-----\n";
+    public static final String PUBLIC_KEY_SUFFIX = "-----END PUBLIC KEY-----";
+    public static final String SSH_RSA_PREFIX = "ssh-rsa ";
+
+    private File publicCerts = null;
+    private boolean jwtInBody = true;
+    private String jwtAttribute;
+    private KeyFactory kf;
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(PUBLIC_KEYS_PATH);
+        descriptors.add(JWT_ATTRIBUTE_NAME);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(SUCCESS_REL);
+        relationships.add(FAILURE_REL);
+        this.relationships = Collections.unmodifiableSet(relationships);
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+        // read public cert from property
+        publicCerts = new File(context.getProperty(PUBLIC_KEYS_PATH).getValue());
+        try {
+            kf = KeyFactory.getInstance("RSA");
+        } catch (NoSuchAlgorithmException e) {
+            context.yield();
+        }
+
+        // read attribute from property
+        jwtAttribute = context.getProperty(JWT_ATTRIBUTE_NAME).getValue();
+        if (!"".equals(jwtAttribute)) {
+            jwtInBody = false;
+        }
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+        FlowFile flowFile = session.get();
+        if (flowFile == null) {
+            return;
+        }
+
+        String jwt;
+        if (jwtInBody) {
+            // read JWT from flowfile
+            final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            session.exportTo(flowFile, bytes);

Review comment:
       I think this might want to use `session.read()` and ensure the correct character encoding is used. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482559869



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/pom.xml
##########
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-jwt-bundle</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-jwt-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>

Review comment:
       Why are these modules scoped to `runtime`?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482557013



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/pom.xml
##########
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-jwt-bundle</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-jwt-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.65</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.65</version>
+        </dependency>
+        <dependency>
+            <groupId>com.hierynomus</groupId>
+            <artifactId>sshj</artifactId>

Review comment:
       What is `sshj` used for in these processors?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482815190



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/CustomValidators.java
##########
@@ -0,0 +1,114 @@
+/*
+ * 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.jwt;
+
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+
+public class CustomValidators {
+
+    public static final Validator DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // path is directory
+            final File directory = new File(input);
+            if (!directory.isDirectory()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be a directory").valid(false).build();
+            }
+            // directory contains at least one ".pub" file
+            final File[] keys = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".pub"));
+            if (keys.length < 1) {
+                return new ValidationResult.Builder().subject(subject).input(input).explanation(
+                        "the directory must contain at least one RSA public key file with the entension \".pub\"")
+                        .valid(false).build();
+            }
+            return new ValidationResult.Builder().subject(subject).input(input)
+                    .explanation("Valid public key directory").valid(true).build();
+        }
+    };
+
+    public static final Validator PRIVATE_KEY_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // is a file
+            final File key = new File(input);
+            if (!key.isFile()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be path to private key, not a directory").valid(false).build();
+            }
+            // file is readable
+            if (!key.canRead()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be readable by the NiFi user").valid(false).build();
+            }
+            // read key from file
+            KeyProvider keyProvider;
+            try (final SSHClient sshClient = new SSHClient()){

Review comment:
       This was the best way I could reliably and easily parse private keys created using either openssh or openssl RSA key generators




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482809817



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {

Review comment:
       The documentation for the [jwt library](https://github.com/jwtk/jjwt#jws-key-rsa) used say the following when talking about signing algorithms:
   
   `It is recommended that you specify the signing key by calling call the JwtBuilder's signWith method and let JJWT determine the most secure algorithm allowed for the specified key.:`




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482800110



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PRIVATE_KEY_PATH = new PropertyDescriptor.Builder()
+            .name("PRIVATE_KEY_PATH").displayName("Private Key").description("Path to private key to sign the JWT. The key must be readable by the NiFi user")
+            .required(true).addValidator(CustomValidators.PRIVATE_KEY_VALIDATOR).build();
+
+    public static final Relationship SUCCESS_REL = new Relationship.Builder().name("success")
+            .description("A FlowFile is routed to this relationship after its JWT has been validated and unsigned")
+            .build();
+
+    public static final Relationship FAILURE_REL = new Relationship.Builder().name("failure").description(
+            "A FlowFile is routed to this relationship if the JWT cannot be validated or unsigned for any reason")
+            .build();
+
+    public static final String FAILURE_REASON_ATTR = "failure.reason";
+    public static final String INVALID_JSON = "Invalid JSON: ";
+    public static final String ERROR_PREFIX = "Error signing JWT: ";
+
+    private PrivateKey privateKey;
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(PRIVATE_KEY_PATH);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(SUCCESS_REL);
+        relationships.add(FAILURE_REL);
+        this.relationships = Collections.unmodifiableSet(relationships);
+
+        if (Security.getProvider("BC") == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+        final File key = new File(context.getProperty(PRIVATE_KEY_PATH).getValue());
+        KeyProvider keyProvider;
+        try (final SSHClient sshClient = new SSHClient()) {
+            keyProvider = sshClient.loadKeys(key.getAbsolutePath());
+            privateKey = keyProvider.getPrivate();
+        } catch (IOException e) {
+            privateKey = null;

Review comment:
       It does with the validationContext set on the property




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482812023



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/test/resources/2048key1.key
##########
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----

Review comment:
       When first testing the processor I wanted to be sure that it handled multiple public keys in the directory correctly.




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#issuecomment-686105410


   Thanks for this contribution. It might feel like I put a lot of comments/questions that are designed to prevent things from being merged. That's not my intent. We get a lot of submissions which work very well for a specific use case and "scratch your own itch" as it were, and people don't evaluate/aren't aware of a lot of the negative/edge cases which can arise in the many scenarios users deploy NiFi into. If you're not interested in trying to address all the possible combinations and failure scenarios, that's ok. We always appreciate sharing these features with the community, and often suggest hosting the code for the custom processors in your own repository so interested users can build and deploy them locally. To be accepted and bundled as part of the core project, however, we have a (non-legally obligated) responsibility to support and maintain the code, so we have to reach a certain threshold of familiarity and basic behavioral consistency in order to do that. Thanks. 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482791885



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-nar/pom.xml
##########
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>

Review comment:
       No, I thought this was the best place for the processors and where they should go but if they should be somewhere else let me know!




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482566534



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PRIVATE_KEY_PATH = new PropertyDescriptor.Builder()
+            .name("PRIVATE_KEY_PATH").displayName("Private Key").description("Path to private key to sign the JWT. The key must be readable by the NiFi user")
+            .required(true).addValidator(CustomValidators.PRIVATE_KEY_VALIDATOR).build();
+
+    public static final Relationship SUCCESS_REL = new Relationship.Builder().name("success")
+            .description("A FlowFile is routed to this relationship after its JWT has been validated and unsigned")
+            .build();
+
+    public static final Relationship FAILURE_REL = new Relationship.Builder().name("failure").description(
+            "A FlowFile is routed to this relationship if the JWT cannot be validated or unsigned for any reason")
+            .build();
+
+    public static final String FAILURE_REASON_ATTR = "failure.reason";
+    public static final String INVALID_JSON = "Invalid JSON: ";
+    public static final String ERROR_PREFIX = "Error signing JWT: ";
+
+    private PrivateKey privateKey;
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(PRIVATE_KEY_PATH);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(SUCCESS_REL);
+        relationships.add(FAILURE_REL);
+        this.relationships = Collections.unmodifiableSet(relationships);
+
+        if (Security.getProvider("BC") == null) {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+        final File key = new File(context.getProperty(PRIVATE_KEY_PATH).getValue());
+        KeyProvider keyProvider;
+        try (final SSHClient sshClient = new SSHClient()) {
+            keyProvider = sshClient.loadKeys(key.getAbsolutePath());
+            privateKey = keyProvider.getPrivate();
+        } catch (IOException e) {
+            privateKey = null;

Review comment:
       I would suggest that failing to obtain the private key should prevent the processor from starting rather than setting this value as a sentinel and yielding during processing. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#issuecomment-686393988


   Thanks for the review @alopresto, I'll try to address all the comments as best I can over the next few weeks when I get some time 


----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482598815



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/test/java/org/apache/nifi/processors/jwt/SignJWTTest.java
##########
@@ -0,0 +1,161 @@
+/*
+ * 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.jwt;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class SignJWTTest {
+
+    final String json = "{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"admin\":true,\"iat\":1516239022,\"testComplexStruct\":{\"even more complex\":[\"and a\",\"bit more\"]}}";
+
+    @Test
+    public void Test() {
+        final String privateKey = "./src/test/resources/4096key2.key";
+        final String pubKeyDir = "./src/test/resources/";
+
+        final String result = signAndUnsignJWTSuccess(json, privateKey, pubKeyDir);
+        System.out.println(result);

Review comment:
       Tests can use a `Logger` and avoid `System.out.println()` calls. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482559401



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-nar/pom.xml
##########
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>

Review comment:
       Was there an intentional decision not to put these processors under `nifi-standard-processors`?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482558181



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/pom.xml
##########
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-jwt-bundle</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-jwt-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.65</version>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk15on</artifactId>
+            <version>1.65</version>
+        </dependency>
+        <dependency>
+            <groupId>com.hierynomus</groupId>
+            <artifactId>sshj</artifactId>
+            <version>0.29.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <version>2.9.8</version>

Review comment:
       Please use `${jackson-databind.version}` to get the latest version (2.9.8 is susceptible to a number of CVEs and 2.9.10.5 is used elsewhere in the project). 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482597711



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {

Review comment:
       Where is the signing algorithm set here?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482562964



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/CustomValidators.java
##########
@@ -0,0 +1,114 @@
+/*
+ * 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.jwt;
+
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+
+public class CustomValidators {
+
+    public static final Validator DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // path is directory
+            final File directory = new File(input);
+            if (!directory.isDirectory()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be a directory").valid(false).build();
+            }
+            // directory contains at least one ".pub" file
+            final File[] keys = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".pub"));
+            if (keys.length < 1) {
+                return new ValidationResult.Builder().subject(subject).input(input).explanation(
+                        "the directory must contain at least one RSA public key file with the entension \".pub\"")
+                        .valid(false).build();
+            }
+            return new ValidationResult.Builder().subject(subject).input(input)
+                    .explanation("Valid public key directory").valid(true).build();
+        }
+    };
+
+    public static final Validator PRIVATE_KEY_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // is a file
+            final File key = new File(input);
+            if (!key.isFile()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be path to private key, not a directory").valid(false).build();
+            }
+            // file is readable
+            if (!key.canRead()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be readable by the NiFi user").valid(false).build();
+            }
+            // read key from file
+            KeyProvider keyProvider;
+            try (final SSHClient sshClient = new SSHClient()){

Review comment:
       I now see this use of `sshj` -- is it necessary or can the private key be loaded and parsed without this dependency?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482800691



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {

Review comment:
       Thank you! Been thinking for a while for a better word but couldn't settle on one




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482799481



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PRIVATE_KEY_PATH = new PropertyDescriptor.Builder()

Review comment:
       Had not considered that case, but can see a use for it. Not depending on the filesystem makes sense, how could that be implemented for the (currently named) UnsignJWT processor?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r490290527



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/CustomValidators.java
##########
@@ -0,0 +1,114 @@
+/*
+ * 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.jwt;
+
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+
+public class CustomValidators {
+
+    public static final Validator DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // path is directory
+            final File directory = new File(input);
+            if (!directory.isDirectory()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be a directory").valid(false).build();
+            }
+            // directory contains at least one ".pub" file
+            final File[] keys = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".pub"));
+            if (keys.length < 1) {
+                return new ValidationResult.Builder().subject(subject).input(input).explanation(
+                        "the directory must contain at least one RSA public key file with the entension \".pub\"")
+                        .valid(false).build();
+            }
+            return new ValidationResult.Builder().subject(subject).input(input)
+                    .explanation("Valid public key directory").valid(true).build();
+        }
+    };
+
+    public static final Validator PRIVATE_KEY_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // is a file
+            final File key = new File(input);
+            if (!key.isFile()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be path to private key, not a directory").valid(false).build();
+            }
+            // file is readable
+            if (!key.canRead()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be readable by the NiFi user").valid(false).build();
+            }
+            // read key from file
+            KeyProvider keyProvider;
+            try (final SSHClient sshClient = new SSHClient()){

Review comment:
       I can use bouncyCastle and standard libraries to read RSA private keys but not those generated using `ssh-keygen -t rsa ...` (which end up with headers of `-----BEGIN OPENSSH PRIVATE KEY-----` instead of `-----BEGIN RSA PRIVATE KEY-----`).
   
   If we want to support these openssh keys it would definitely be worth writing internal methods but I am unsure where these should be, and think it would be useful to do so on a new JIRA ticket and PR...
   
   I couldn't find anything that jumped out at me from the `nidi-security-utils` docs




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482860683



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/pom.xml
##########
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-jwt-bundle</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-jwt-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>

Review comment:
       good spot, not sure why they are - don't think its needed




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482556467



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/pom.xml
##########
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-jwt-bundle</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-jwt-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-jackson</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcpkix-jdk15on</artifactId>
+            <version>1.65</version>

Review comment:
       I think this should be 1.66 now to be consistent with other uses of BouncyCastle throughout the project. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482598701



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/test/java/org/apache/nifi/processors/jwt/SignJWTTest.java
##########
@@ -0,0 +1,161 @@
+/*
+ * 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.jwt;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.List;
+
+import org.apache.nifi.util.MockFlowFile;
+import org.apache.nifi.util.TestRunner;
+import org.apache.nifi.util.TestRunners;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class SignJWTTest {
+
+    final String json = "{\"sub\":\"1234567890\",\"name\":\"John Doe\",\"admin\":true,\"iat\":1516239022,\"testComplexStruct\":{\"even more complex\":[\"and a\",\"bit more\"]}}";
+
+    @Test
+    public void Test() {

Review comment:
       JUnit test methods should start with a lowercase character and follow consistent camelcase style similar to other Java methods. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482569837



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {

Review comment:
       I don't know that "unsign" is the right terminology here -- I would suggest `VerifyJWT` instead. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482860683



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/pom.xml
##########
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.nifi</groupId>
+        <artifactId>nifi-jwt-bundle</artifactId>
+        <version>1.13.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>nifi-jwt-processors</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-utils</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.nifi</groupId>
+            <artifactId>nifi-mock</artifactId>
+            <version>1.13.0-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-api</artifactId>
+            <version>0.11.1</version>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt-impl</artifactId>
+            <version>0.11.1</version>
+            <scope>runtime</scope>

Review comment:
       good spot, not sure why they are - don't think that scope is needed




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482864287



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")
+            .displayName("Approved Public Keys")
+            .description("Path to the directory containing approved public certificates. Certificates are expected to have "
+                + "extension \".pub\" and be x509 PEM files")
+            .required(true).addValidator(CustomValidators.DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR).build();
+
+    public static final PropertyDescriptor JWT_ATTRIBUTE_NAME = new PropertyDescriptor.Builder().name("JWT_ATTRIBUTE_NAME")

Review comment:
       No reason I can think of, will add EL support :)




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482599579



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/test/resources/2048key1.key
##########
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----

Review comment:
       Why are there multiple keys for each key-length?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482802959



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")

Review comment:
       What would be the best way to go about setting multiple public keys in the property? Just as multiple separate properties?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482565027



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PRIVATE_KEY_PATH = new PropertyDescriptor.Builder()

Review comment:
       Did you evaluate the use case where a user would provide the private key in ASCII format directly into the component property? We're trying to move away from exclusively relying on the filesystem for assets because deploying these to a cluster is difficult and interferes with elastic scaling & containerization. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482601829



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {

Review comment:
       This also doesn't seem to support ECDSA or HMAC signing algorithms. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482561243



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/CustomValidators.java
##########
@@ -0,0 +1,114 @@
+/*
+ * 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.jwt;
+
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+
+public class CustomValidators {
+
+    public static final Validator DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {

Review comment:
       I'm not sure I understand the purpose of returning here if EL is present?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] github-actions[bot] closed pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
github-actions[bot] closed pull request #4495:
URL: https://github.com/apache/nifi/pull/4495


   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482809817



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {

Review comment:
       The documentation for the jwt library used say the following when talking about signing algorithms:
   
   `It is recommended that you specify the signing key by calling call the JwtBuilder's signWith method and let JJWT determine the most secure algorithm allowed for the specified key.:`




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r485225060



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/CustomValidators.java
##########
@@ -0,0 +1,114 @@
+/*
+ * 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.jwt;
+
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.components.Validator;
+import org.apache.nifi.processor.util.StandardValidators;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+
+public class CustomValidators {
+
+    public static final Validator DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // path is directory
+            final File directory = new File(input);
+            if (!directory.isDirectory()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be a directory").valid(false).build();
+            }
+            // directory contains at least one ".pub" file
+            final File[] keys = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".pub"));
+            if (keys.length < 1) {
+                return new ValidationResult.Builder().subject(subject).input(input).explanation(
+                        "the directory must contain at least one RSA public key file with the entension \".pub\"")
+                        .valid(false).build();
+            }
+            return new ValidationResult.Builder().subject(subject).input(input)
+                    .explanation("Valid public key directory").valid(true).build();
+        }
+    };
+
+    public static final Validator PRIVATE_KEY_VALIDATOR = new Validator() {
+        @Override
+        public ValidationResult validate(String subject, String input, ValidationContext context) {
+            // expression language
+            if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation("Expression Language Present").valid(true).build();
+            }
+            // not empty
+            ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input,
+                    context);
+            if (!nonEmptyValidatorResult.isValid()) {
+                return nonEmptyValidatorResult;
+            }
+            // valid path
+            ValidationResult pathValidatorResult = StandardValidators.FILE_EXISTS_VALIDATOR.validate(subject, input,
+                    context);
+            if (!pathValidatorResult.isValid()) {
+                return pathValidatorResult;
+            }
+            // is a file
+            final File key = new File(input);
+            if (!key.isFile()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be path to private key, not a directory").valid(false).build();
+            }
+            // file is readable
+            if (!key.canRead()) {
+                return new ValidationResult.Builder().subject(subject).input(input)
+                        .explanation(subject + " must be readable by the NiFi user").valid(false).build();
+            }
+            // read key from file
+            KeyProvider keyProvider;
+            try (final SSHClient sshClient = new SSHClient()){

Review comment:
       I think we should write internal methods to perform this to avoid bringing in an unnecessary dependency. I think we already have some asymmetric key parsing utilities in `nifi-security-utils`. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482571304



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")

Review comment:
       Again, this could be expanded to allow direct population of the public key, and many public keys are not provided in `.pub` files -- perhaps the default extension filter could be `*.pub` but allow for a regex like `*.pub|*.pem`, etc. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482596438



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")
+            .displayName("Approved Public Keys")
+            .description("Path to the directory containing approved public certificates. Certificates are expected to have "
+                + "extension \".pub\" and be x509 PEM files")
+            .required(true).addValidator(CustomValidators.DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR).build();
+
+    public static final PropertyDescriptor JWT_ATTRIBUTE_NAME = new PropertyDescriptor.Builder().name("JWT_ATTRIBUTE_NAME")
+            .displayName("JWT Attribute name")
+            .description("The name of the attribute which contains the JWT. Leave empty to read JWT from the flowfile content")
+            .required(false).defaultValue("").addValidator(StandardValidators.ATTRIBUTE_KEY_PROPERTY_NAME_VALIDATOR).build();
+
+    public static final Relationship SUCCESS_REL = new Relationship.Builder().name("success")
+            .description("A FlowFile is routed to this relationship after its JWT has been validated and unsigned")
+            .build();
+
+    public static final Relationship FAILURE_REL = new Relationship.Builder().name("failure").description(
+            "A FlowFile is routed to this relationship if the JWT cannot be validated or unsigned for any reason")
+            .build();
+
+    public static final String HEADER_ATTRIBUTE = "jwt.header";
+    public static final String FOOTER_ATTRIBUTE = "jwt.footer";
+    public static final String JWT_PREFIX_ATTRIBUTE = "jwt.";
+
+    public static final String PUBLIC_KEY_PREFIX = "-----BEGIN PUBLIC KEY-----\n";
+    public static final String PUBLIC_KEY_SUFFIX = "-----END PUBLIC KEY-----";
+    public static final String SSH_RSA_PREFIX = "ssh-rsa ";
+
+    private File publicCerts = null;
+    private boolean jwtInBody = true;
+    private String jwtAttribute;
+    private KeyFactory kf;
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(PUBLIC_KEYS_PATH);
+        descriptors.add(JWT_ATTRIBUTE_NAME);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(SUCCESS_REL);
+        relationships.add(FAILURE_REL);
+        this.relationships = Collections.unmodifiableSet(relationships);
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+        // read public cert from property
+        publicCerts = new File(context.getProperty(PUBLIC_KEYS_PATH).getValue());
+        try {
+            kf = KeyFactory.getInstance("RSA");
+        } catch (NoSuchAlgorithmException e) {
+            context.yield();
+        }
+
+        // read attribute from property
+        jwtAttribute = context.getProperty(JWT_ATTRIBUTE_NAME).getValue();
+        if (!"".equals(jwtAttribute)) {
+            jwtInBody = false;
+        }
+    }
+
+    @Override
+    public void onTrigger(final ProcessContext context, final ProcessSession session) throws ProcessException {
+        FlowFile flowFile = session.get();
+        if (flowFile == null) {
+            return;
+        }
+
+        String jwt;
+        if (jwtInBody) {
+            // read JWT from flowfile
+            final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+            session.exportTo(flowFile, bytes);
+            jwt = bytes.toString();
+        } else {
+            jwt = flowFile.getAttribute(jwtAttribute);
+        }
+
+        for (final File cert : publicCerts.listFiles((dir, name) -> name.toLowerCase().endsWith(".pub"))) {

Review comment:
       The acquisition of the public key should be refactored to a separate method and should be run outside of the `onTrigger()` method for performance reasons. The selected key(s) can be cached rather than being loaded and each attempted for every flowfile that enters the processor. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] github-actions[bot] commented on pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
github-actions[bot] commented on pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#issuecomment-819929771


   We're marking this PR as stale due to lack of updates in the past few months. If after another couple of weeks the stale label has not been removed this PR will be closed. This stale marker and eventual auto close does not indicate a judgement of the PR just lack of reviewer bandwidth and helps us keep the PR queue more manageable.  If you would like this PR re-opened you can do so and a committer can remove the stale tag.  Or you can open a new PR.  Try to help review other PRs to increase PR review bandwidth which in turn helps yours.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] dml872 commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
dml872 commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482799481



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/SignJWT.java
##########
@@ -0,0 +1,165 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import net.schmizz.sshj.SSHClient;
+import net.schmizz.sshj.userauth.keyprovider.KeyProvider;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "sign" })
+@CapabilityDescription("JWT Signs a JSON file with a specified private key. The JSON must be key value pairs.")
+@WritesAttributes({
+    @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+            + "the attribute will contain the error message resulting from the validation failure.")
+})
+@SeeAlso({ UnsignJWT.class })
+public class SignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PRIVATE_KEY_PATH = new PropertyDescriptor.Builder()

Review comment:
       Had not considered that case, but can see a use for it. Not depending on the filesystem makes sense, how could that be implemented for the (currently named) UnsignJWT processor?
   
   EDIT - ah discussed in a later issue :) 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482586702



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")
+            .displayName("Approved Public Keys")
+            .description("Path to the directory containing approved public certificates. Certificates are expected to have "
+                + "extension \".pub\" and be x509 PEM files")
+            .required(true).addValidator(CustomValidators.DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR).build();
+
+    public static final PropertyDescriptor JWT_ATTRIBUTE_NAME = new PropertyDescriptor.Builder().name("JWT_ATTRIBUTE_NAME")

Review comment:
       Is there a reason we don't want to support Expression Language in this property?




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org



[GitHub] [nifi] alopresto commented on a change in pull request #4495: NIFI-7475 JWT processor bundle

Posted by GitBox <gi...@apache.org>.
alopresto commented on a change in pull request #4495:
URL: https://github.com/apache/nifi/pull/4495#discussion_r482592013



##########
File path: nifi-nar-bundles/nifi-jwt-bundle/nifi-jwt-processors/src/main/java/org/apache/nifi/processors/jwt/UnsignJWT.java
##########
@@ -0,0 +1,236 @@
+/*
+ * 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.jwt;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import io.jsonwebtoken.Jws;
+import io.jsonwebtoken.JwtException;
+import io.jsonwebtoken.Jwts;
+import org.apache.nifi.annotation.behavior.InputRequirement;
+import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
+import org.apache.nifi.annotation.behavior.SideEffectFree;
+import org.apache.nifi.annotation.behavior.WritesAttribute;
+import org.apache.nifi.annotation.behavior.WritesAttributes;
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.SeeAlso;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.annotation.lifecycle.OnScheduled;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.processor.AbstractProcessor;
+import org.apache.nifi.processor.Relationship;
+import org.apache.nifi.processor.ProcessorInitializationContext;
+import org.apache.nifi.processor.ProcessContext;
+import org.apache.nifi.processor.ProcessSession;
+import org.apache.nifi.processor.exception.ProcessException;
+import org.apache.nifi.processor.io.OutputStreamCallback;
+import org.apache.nifi.processor.util.StandardValidators;
+import org.bouncycastle.util.encoders.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.List;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map.Entry;
+
+@InputRequirement(Requirement.INPUT_REQUIRED)
+@SideEffectFree
+@Tags({ "jwt", "json", "flowfile", "unsign" })
+@CapabilityDescription("Validates and unsigns a JWT either from the flowfile body or from an attribute. The JWT is unsigned "
+                + "using a directory of public RSA keys specified by the \"Approved Public Keys\" property. By default the "
+                + "processor will read the JWT from the content of the flowfile and rewrite the contents with the JWT claims "
+                + "in JSON format. If an attribute name is given as the \"JWT Attribute Name\" property then the content of "
+                + "that attribute will be unsigned and the JWT claims will be written as \"jwt.XXX\" attributes.")
+@WritesAttributes({
+        @WritesAttribute(attribute = "jwt.header", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the the header of the JWT."),
+        @WritesAttribute(attribute = "jwt.footer", description = "If the flow file is routed to the success relationship "
+                + "the attribute will contain the footer of the JWT."),
+            @WritesAttribute(attribute = "jwt.XXX", description = "If the flow file is routed to the success relationship "
+                + "and the JWT was unsugned from an attribute, these attributes will contain the claims of the JWT."),
+        @WritesAttribute(attribute = "failure.reason", description = "If the flow file is routed to the failure relationship "
+                + "the attribute will contain the error message resulting from the validation failure.") })
+@SeeAlso({ SignJWT.class })
+public class UnsignJWT extends AbstractProcessor {
+
+    public static final PropertyDescriptor PUBLIC_KEYS_PATH = new PropertyDescriptor.Builder().name("PUBLIC_KEYS_PATH")
+            .displayName("Approved Public Keys")
+            .description("Path to the directory containing approved public certificates. Certificates are expected to have "
+                + "extension \".pub\" and be x509 PEM files")
+            .required(true).addValidator(CustomValidators.DIRECTORY_HAS_PUBLIC_KEYS_VALIDATOR).build();
+
+    public static final PropertyDescriptor JWT_ATTRIBUTE_NAME = new PropertyDescriptor.Builder().name("JWT_ATTRIBUTE_NAME")
+            .displayName("JWT Attribute name")
+            .description("The name of the attribute which contains the JWT. Leave empty to read JWT from the flowfile content")
+            .required(false).defaultValue("").addValidator(StandardValidators.ATTRIBUTE_KEY_PROPERTY_NAME_VALIDATOR).build();
+
+    public static final Relationship SUCCESS_REL = new Relationship.Builder().name("success")
+            .description("A FlowFile is routed to this relationship after its JWT has been validated and unsigned")
+            .build();
+
+    public static final Relationship FAILURE_REL = new Relationship.Builder().name("failure").description(
+            "A FlowFile is routed to this relationship if the JWT cannot be validated or unsigned for any reason")
+            .build();
+
+    public static final String HEADER_ATTRIBUTE = "jwt.header";
+    public static final String FOOTER_ATTRIBUTE = "jwt.footer";
+    public static final String JWT_PREFIX_ATTRIBUTE = "jwt.";
+
+    public static final String PUBLIC_KEY_PREFIX = "-----BEGIN PUBLIC KEY-----\n";
+    public static final String PUBLIC_KEY_SUFFIX = "-----END PUBLIC KEY-----";
+    public static final String SSH_RSA_PREFIX = "ssh-rsa ";
+
+    private File publicCerts = null;
+    private boolean jwtInBody = true;
+    private String jwtAttribute;
+    private KeyFactory kf;
+
+    private List<PropertyDescriptor> descriptors;
+    private Set<Relationship> relationships;
+
+    @Override
+    protected void init(final ProcessorInitializationContext context) {
+        final List<PropertyDescriptor> descriptors = new ArrayList<PropertyDescriptor>();
+        descriptors.add(PUBLIC_KEYS_PATH);
+        descriptors.add(JWT_ATTRIBUTE_NAME);
+        this.descriptors = Collections.unmodifiableList(descriptors);
+
+        final Set<Relationship> relationships = new HashSet<Relationship>();
+        relationships.add(SUCCESS_REL);
+        relationships.add(FAILURE_REL);
+        this.relationships = Collections.unmodifiableSet(relationships);
+    }
+
+    @Override
+    public Set<Relationship> getRelationships() {
+        return this.relationships;
+    }
+
+    @Override
+    public final List<PropertyDescriptor> getSupportedPropertyDescriptors() {
+        return descriptors;
+    }
+
+    @OnScheduled
+    public void onScheduled(final ProcessContext context) {
+        // read public cert from property
+        publicCerts = new File(context.getProperty(PUBLIC_KEYS_PATH).getValue());
+        try {

Review comment:
       If the JVM doesn't have the RSA algorithm available, it should fail and communicate that to the user. 




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
users@infra.apache.org