NIFI-2123: Add authorization of provenance events; refactor core classes so 
that Authorizable is located within nifi-api. This closes #592


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

Branch: refs/heads/master
Commit: ae9e2fdf0bfce31c38020784b78171709618903c
Parents: 65d8958
Author: Mark Payne <[email protected]>
Authored: Tue Jun 28 14:53:35 2016 -0400
Committer: Matt Gilman <[email protected]>
Committed: Thu Jun 30 07:57:17 2016 -0400

----------------------------------------------------------------------
 .../authorization/AccessDeniedException.java    |  39 ++
 .../authorization/resource/Authorizable.java    | 125 ++++++
 .../nifi/authorization/user/NiFiUser.java       |  44 ++
 .../ProvenanceAuthorizableFactory.java          |  35 ++
 .../provenance/ProvenanceEventRepository.java   |  98 +++--
 .../nifi/provenance/ProvenanceEventType.java    |   8 +-
 .../lineage/ComputeLineageSubmission.java       |   5 +
 .../nifi/provenance/search/QuerySubmission.java |   5 +
 .../nifi/provenance/AsyncLineageSubmission.java |   9 +-
 .../nifi/provenance/AsyncQuerySubmission.java   |   9 +-
 .../provenance/PlaceholderProvenanceEvent.java  | 196 +++++++++
 .../nifi/provenance/StandardLineageResult.java  |   8 +-
 .../nifi/provenance/StandardQueryResult.java    |  14 +-
 .../MockProvenanceEventRepository.java          |  24 +-
 .../api/dto/provenance/lineage/LineageDTO.java  |  15 -
 .../provenance/lineage/LineageRequestDTO.java   |  13 +
 .../authorization/AccessDeniedException.java    |  39 --
 .../authorization/resource/Authorizable.java    | 141 -------
 .../resource/ProvenanceEventAuthorizable.java   |  41 ++
 .../authorization/resource/ResourceFactory.java |  25 +-
 .../authorization/resource/ResourceType.java    |   1 +
 .../nifi/authorization/user/NiFiUser.java       |  93 -----
 .../authorization/user/StandardNiFiUser.java    |  94 +++++
 .../org/apache/nifi/controller/Template.java    |  15 +-
 .../apache/nifi/controller/FlowController.java  | 107 +++--
 .../nifi/web/StandardNiFiServiceFacade.java     |   2 +-
 .../StandardNiFiWebConfigurationContext.java    |  18 +-
 .../nifi/web/api/AccessPolicyResource.java      |   9 +-
 .../nifi/web/api/ApplicationResource.java       |   4 +-
 .../apache/nifi/web/api/ConnectionResource.java |   7 +-
 .../nifi/web/api/ControllerServiceResource.java |  73 ++--
 .../nifi/web/api/FlowFileQueueResource.java     |  17 +-
 .../org/apache/nifi/web/api/FlowResource.java   |  66 +--
 .../org/apache/nifi/web/api/FunnelResource.java |   7 +-
 .../apache/nifi/web/api/InputPortResource.java  |   7 +-
 .../org/apache/nifi/web/api/LabelResource.java  |   7 +-
 .../apache/nifi/web/api/OutputPortResource.java |   7 +-
 .../nifi/web/api/ProcessGroupResource.java      | 125 +++---
 .../apache/nifi/web/api/ProcessorResource.java  |  13 +-
 .../apache/nifi/web/api/ProvenanceResource.java |   8 +-
 .../web/api/RemoteProcessGroupResource.java     |  11 +-
 .../nifi/web/api/ReportingTaskResource.java     |  13 +-
 .../apache/nifi/web/api/SnippetResource.java    |   3 +-
 .../apache/nifi/web/api/TemplateResource.java   |   5 +-
 .../apache/nifi/web/api/TenantsResource.java    |  21 +-
 .../api/config/AccessDeniedExceptionMapper.java |   8 +-
 .../org/apache/nifi/web/api/dto/DtoFactory.java |   7 +-
 .../nifi/web/controller/ControllerFacade.java   |  96 ++---
 .../web/dao/impl/StandardConnectionDAO.java     |   4 +-
 .../web/StandardNiFiServiceFacadeSpec.groovy    |   9 +-
 .../web/revision/TestNaiveRevisionManager.java  |   7 +-
 .../anonymous/NiFiAnonymousUserFilter.java      |   8 +-
 .../security/jwt/JwtAuthenticationProvider.java |   6 +-
 .../security/otp/OtpAuthenticationProvider.java |   3 +-
 .../security/token/NiFiAuthenticationToken.java |   5 +
 .../x509/X509AuthenticationProvider.java        |   5 +-
 .../js/nf/provenance/nf-provenance-lineage.js   |   8 +-
 .../js/nf/provenance/nf-provenance-table.js     |   4 +-
 .../webapp/js/nf/provenance/nf-provenance.js    |  25 +-
 .../src/main/webapp/js/nf/summary/nf-summary.js |  23 +-
 .../PersistentProvenanceRepository.java         | 188 +++++++--
 .../authorization/AuthorizationCheck.java       |  24 ++
 .../nifi/provenance/lucene/DocsReader.java      |  13 +-
 .../nifi/provenance/lucene/IndexSearch.java     |  10 +-
 .../nifi/provenance/lucene/LineageQuery.java    |   7 +-
 .../TestPersistentProvenanceRepository.java     | 400 +++++++++++++++++--
 .../VolatileProvenanceRepository.java           | 168 ++++++--
 .../TestVolatileProvenanceRepository.java       |  28 +-
 68 files changed, 1832 insertions(+), 850 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
 
b/nifi-api/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
new file mode 100644
index 0000000..8712d28
--- /dev/null
+++ 
b/nifi-api/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
@@ -0,0 +1,39 @@
+/*
+ * 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.authorization;
+
+/**
+ * Represents any error that might occur while authorizing user requests.
+ */
+public class AccessDeniedException extends RuntimeException {
+    private static final long serialVersionUID = -5683444815269084134L;
+
+    public AccessDeniedException(Throwable cause) {
+        super(cause);
+    }
+
+    public AccessDeniedException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public AccessDeniedException(String message) {
+        super(message);
+    }
+
+    public AccessDeniedException() {
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
 
b/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
new file mode 100644
index 0000000..09fab19
--- /dev/null
+++ 
b/nifi-api/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
@@ -0,0 +1,125 @@
+/*
+ * 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.authorization.resource;
+
+import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.authorization.AuthorizationRequest;
+import org.apache.nifi.authorization.AuthorizationResult;
+import org.apache.nifi.authorization.AuthorizationResult.Result;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.authorization.user.NiFiUser;
+
+public interface Authorizable {
+
+    /**
+     * The parent for this Authorizable. May be null.
+     *
+     * @return the parent authorizable or null
+     */
+    Authorizable getParentAuthorizable();
+
+    /**
+     * The Resource for this Authorizable.
+     *
+     * @return the parent resource
+     */
+    Resource getResource();
+
+    /**
+     * Returns whether the current user is authorized for the specified action 
on the specified resource. This
+     * method does not imply the user is directly attempting to access the 
specified resource. If the user is
+     * attempting a direct access use Authorizable.authorize().
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @return is authorized
+     */
+    default boolean isAuthorized(Authorizer authorizer, RequestAction action, 
NiFiUser user) {
+        return Result.Approved.equals(checkAuthorization(authorizer, action, 
user).getResult());
+    }
+
+    /**
+     * Returns the result of an authorization request for the specified user 
for the specified action on the specified
+     * resource. This method does not imply the user is directly attempting to 
access the specified resource. If the user is
+     * attempting a direct access use Authorizable.authorize().
+     *
+     * @param authorizer authorizer
+     * @param action action
+     * @param user user
+     * @return is authorized
+     */
+    default AuthorizationResult checkAuthorization(Authorizer authorizer, 
RequestAction action, NiFiUser user) {
+        // TODO - include user details context
+
+        // build the request
+        final AuthorizationRequest request = new AuthorizationRequest.Builder()
+                .identity(user.getIdentity())
+                .anonymous(user.isAnonymous())
+                .accessAttempt(false)
+                .action(action)
+                .resource(getResource())
+                .build();
+
+        // perform the authorization
+        final AuthorizationResult result = authorizer.authorize(request);
+
+        // verify the results
+        if (Result.ResourceNotFound.equals(result.getResult())) {
+            final Authorizable parent = getParentAuthorizable();
+            if (parent == null) {
+                return AuthorizationResult.denied();
+            } else {
+                return parent.checkAuthorization(authorizer, action, user);
+            }
+        } else {
+            return result;
+        }
+    }
+
+    /**
+     * Authorizes the current user for the specified action on the specified 
resource. This method does imply the user is
+     * directly accessing the specified resource.
+     *
+     * @param authorizer authorizer
+     * @param action action
+     */
+    default void authorize(Authorizer authorizer, RequestAction action, 
NiFiUser user) throws AccessDeniedException {
+        // TODO - include user details context
+
+        final AuthorizationRequest request = new AuthorizationRequest.Builder()
+            .identity(user.getIdentity())
+            .anonymous(user.isAnonymous())
+            .accessAttempt(true)
+            .action(action)
+            .resource(getResource())
+            .build();
+
+        final AuthorizationResult result = authorizer.authorize(request);
+        if (Result.ResourceNotFound.equals(result.getResult())) {
+            final Authorizable parent = getParentAuthorizable();
+            if (parent == null) {
+                throw new AccessDeniedException("Access is denied");
+            } else {
+                parent.authorize(authorizer, action, user);
+            }
+        } else if (Result.Denied.equals(result.getResult())) {
+            throw new AccessDeniedException(result.getExplanation());
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java 
b/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
new file mode 100644
index 0000000..d5dee54
--- /dev/null
+++ b/nifi-api/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
@@ -0,0 +1,44 @@
+/*
+ * 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.authorization.user;
+
+/**
+ * A representation of a NiFi user that has logged into the application
+ */
+public interface NiFiUser {
+
+    /**
+     * @return the unique identity of this user
+     */
+    String getIdentity();
+
+    /**
+     * @return the user name for this user
+     */
+    String getUserName();
+
+    /**
+     * @return the next user in the proxied entities chain, or 
<code>null</code> if no more users exist in the chain.
+     */
+    NiFiUser getChain();
+
+    /**
+     * @return <code>true</code> if the user is the unauthenticated Anonymous 
user
+     */
+    boolean isAnonymous();
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceAuthorizableFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceAuthorizableFactory.java
 
b/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceAuthorizableFactory.java
new file mode 100644
index 0000000..23d6b3d
--- /dev/null
+++ 
b/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceAuthorizableFactory.java
@@ -0,0 +1,35 @@
+/*
+ * 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.provenance;
+
+import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.web.ResourceNotFoundException;
+
+public interface ProvenanceAuthorizableFactory {
+
+    /**
+     * Generates an Authorizable object for the Provenance events of the 
component with the given ID
+     *
+     * @param componentId the ID of the component to which the Provenance 
events belong
+     *
+     * @return the Authorizable that can be use to authorize access to 
provenance events
+     * @throws ResourceNotFoundException if no component can be found with the 
given ID
+     */
+    Authorizable createProvenanceAuthorizable(String componentId);
+
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventRepository.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventRepository.java
 
b/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventRepository.java
index 25563b7..235a1fa 100644
--- 
a/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventRepository.java
+++ 
b/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventRepository.java
@@ -19,11 +19,15 @@ package org.apache.nifi.provenance;
 import java.io.IOException;
 import java.util.List;
 
+import org.apache.nifi.authorization.AccessDeniedException;
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.events.EventReporter;
 import org.apache.nifi.provenance.lineage.ComputeLineageSubmission;
 import org.apache.nifi.provenance.search.Query;
 import org.apache.nifi.provenance.search.QuerySubmission;
 import org.apache.nifi.provenance.search.SearchableField;
+import org.apache.nifi.web.ResourceNotFoundException;
 
 /**
  * This Repository houses Provenance Events. The repository is responsible for
@@ -38,9 +42,12 @@ public interface ProvenanceEventRepository {
      * framework.
      *
      * @param eventReporter to report to
+     * @param authorizer the authorizer to use for authorizing individual 
events
+     * @param resourceFactory the resource factory to use for generating 
Provenance Resource objects for authorization purposes
+     *
      * @throws java.io.IOException if unable to initialize
      */
-    void initialize(EventReporter eventReporter) throws IOException;
+    void initialize(EventReporter eventReporter, Authorizer authorizer, 
ProvenanceAuthorizableFactory resourceFactory) throws IOException;
 
     /**
      * Returns a {@link ProvenanceEventBuilder} that is capable of building
@@ -74,7 +81,8 @@ public interface ProvenanceEventRepository {
     /**
      * Returns a List of all <code>ProvenanceEventRecord</code>s in the
      * repository starting with the given ID. The first ID in the repository
-     * will always be 0 or higher.
+     * will always be 0 or higher. This method performs no authorization of
+     * the events.
      *
      * @param firstRecordId id of the first record to retrieve
      * @param maxRecords maximum number of records to retrieve
@@ -84,8 +92,24 @@ public interface ProvenanceEventRepository {
     List<ProvenanceEventRecord> getEvents(long firstRecordId, final int 
maxRecords) throws IOException;
 
     /**
+     * Returns a List of all <code>ProvenanceEventRecord</code>s in the
+     * repository starting with the given ID. The first ID in the repository
+     * will always be 0 or higher. Each event that is found will be authorized
+     * against the given NiFiUser. If the user does not have authorization for
+     * the event, the event will not be returned.
+     *
+     * @param firstRecordId id of the first record to retrieve
+     * @param maxRecords maximum number of records to retrieve
+     * @param user the NiFi user that the events should be authorized against
+     *
+     * @return records
+     * @throws java.io.IOException if error reading from repository
+     */
+    List<ProvenanceEventRecord> getEvents(long firstRecordId, final int 
maxRecords, NiFiUser user) throws IOException;
+
+    /**
      * @return the largest ID of any event that is queryable in the repository.
-     * If no queryable events exists, returns null
+     *         If no queryable events exists, returns null
      */
     Long getMaxEventId();
 
@@ -94,70 +118,96 @@ public interface ProvenanceEventRepository {
      * identifier that can be used to fetch the results at a later time
      *
      * @param query to submit
+     * @param user the NiFi User to authorize the events against
+     *
      * @return an identifier that can be used to fetch the results at a later
-     * time
+     *         time
      */
-    QuerySubmission submitQuery(Query query);
+    QuerySubmission submitQuery(Query query, NiFiUser user);
 
     /**
      * @param queryIdentifier of the query
+     * @param user the user who is retrieving the query
      *
      * @return the QueryResult associated with the given identifier, if the
-     * query has finished processing. If the query has not yet finished 
running,
-     * returns <code>null</code>
+     *         query has finished processing. If the query has not yet 
finished running,
+     *         returns <code>null</code>
      */
-    QuerySubmission retrieveQuerySubmission(String queryIdentifier);
+    QuerySubmission retrieveQuerySubmission(String queryIdentifier, NiFiUser 
user);
 
     /**
      * Submits a Lineage Computation to be completed and returns the
      * AsynchronousLineageResult that indicates the status of the request and
-     * the results, if the computation is complete.
+     * the results, if the computation is complete. If the given user does not
+     * have authorization to view one of the events in the lineage, a 
placeholder
+     * event will be used instead that provides none of the event details 
except
+     * for the identifier of the component that emitted the Provenance Event. 
It is
+     * necessary to include this node in the lineage view so that the lineage 
makes
+     * sense, rather than showing disconnected graphs when the user is not 
authorized
+     * for all components' provenance events.
      *
      * @param flowFileUuid the UUID of the FlowFile for which the Lineage 
should
-     * be calculated
+     *            be calculated
+     * @param user the NiFi User to authorize events against
+     *
      * @return a {@link ComputeLineageSubmission} object that can be used to
-     * check if the computing is complete and if so get the results
+     *         check if the computing is complete and if so get the results
      */
-    ComputeLineageSubmission submitLineageComputation(String flowFileUuid);
+    ComputeLineageSubmission submitLineageComputation(String flowFileUuid, 
NiFiUser user);
 
     /**
      * @param lineageIdentifier identifier of lineage to compute
+     * @param user the user who is retrieving the lineage submission
+     *
      * @return the {@link ComputeLineageSubmission} associated with the given
-     * identifier
+     *         identifier
      */
-    ComputeLineageSubmission retrieveLineageSubmission(String 
lineageIdentifier);
+    ComputeLineageSubmission retrieveLineageSubmission(String 
lineageIdentifier, NiFiUser user);
 
     /**
+     * Retrieves the Provenance Event with the given ID. The event will be 
returned only
+     * if the given user is authorized to access the event. Otherwise, an
+     * AccessDeniedException or ResourceNotFoundException will be thrown, as 
appropriate
+     *
      * @param id to lookup
      * @return the Provenance Event Record with the given ID, if it exists, or
-     * {@code null} otherwise
+     *         {@code null} otherwise
      * @throws IOException if failure while retrieving event
+     * @throws AccessDeniedException if the user does not have access to the 
component
+     * @throws ResourceNotFoundException if the component that the event 
belongs to cannot be found
      */
-    ProvenanceEventRecord getEvent(long id) throws IOException;
+    ProvenanceEventRecord getEvent(long id, NiFiUser user) throws IOException;
 
     /**
-     * Submits a request to expand the parents of the event with the given id
+     * Submits a request to expand the parents of the event with the given id. 
If the given user
+     * is not authorized to access any event, a placeholder will be used 
instead that contains only
+     * the ID of the component that emitted the event.
      *
      * @param eventId the one-up id of the Event to expand
+     * @param user the NiFi user to authorize events against
      * @return a submission which can be checked for status
      *
      * @throws IllegalArgumentException if the given identifier identifies a
-     * Provenance Event that has a Type that is not expandable or if the
-     * identifier cannot be found
+     *             Provenance Event that has a Type that is not expandable or 
if the
+     *             identifier cannot be found
      */
-    ComputeLineageSubmission submitExpandParents(long eventId);
+    ComputeLineageSubmission submitExpandParents(long eventId, NiFiUser user);
 
     /**
-     * Submits a request to expand the children of the event with the given id
+     * Submits a request to expand the children of the event with the given 
id. If the given user
+     * is not authorized to access any event, a placeholder will be used 
instead that contains only
+     * the ID of the component that emitted the event.
      *
      * @param eventId the one-up id of the Event
+     * @param user the NiFi user to authorize events against
+     *
      * @return a submission which can be checked for status
      *
      * @throws IllegalArgumentException if the given identifier identifies a
-     * Provenance Event that has a Type that is not expandable or if the
-     * identifier cannot be found
+     *             Provenance Event that has a Type that is not expandable or 
if the
+     *             identifier cannot be found
      */
-    ComputeLineageSubmission submitExpandChildren(long eventId);
+    ComputeLineageSubmission submitExpandChildren(long eventId, NiFiUser user);
 
     /**
      * Closes the repository, freeing any resources

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventType.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventType.java 
b/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventType.java
index 0d844b8..756d362 100644
--- a/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventType.java
+++ b/nifi-api/src/main/java/org/apache/nifi/provenance/ProvenanceEventType.java
@@ -117,5 +117,11 @@ public enum ProvenanceEventType {
      * that is the UUID of the a newly created FlowFile that will be re-queued
      * for processing.
      */
-    REPLAY
+    REPLAY,
+
+    /**
+     * Indicates that the type of the provenance event is unknown because the 
user
+     * who is attempting to access the event is not authorize to know the type.
+     */
+    UNKNOWN;
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/provenance/lineage/ComputeLineageSubmission.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/provenance/lineage/ComputeLineageSubmission.java
 
b/nifi-api/src/main/java/org/apache/nifi/provenance/lineage/ComputeLineageSubmission.java
index a9df26c..8a11523 100644
--- 
a/nifi-api/src/main/java/org/apache/nifi/provenance/lineage/ComputeLineageSubmission.java
+++ 
b/nifi-api/src/main/java/org/apache/nifi/provenance/lineage/ComputeLineageSubmission.java
@@ -34,6 +34,11 @@ public interface ComputeLineageSubmission {
     Date getSubmissionTime();
 
     /**
+     * @return the identity of the user who submitted the request
+     */
+    String getSubmitterIdentity();
+
+    /**
      * @return the generated identifier for this lineage result
      */
     String getLineageIdentifier();

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-api/src/main/java/org/apache/nifi/provenance/search/QuerySubmission.java
----------------------------------------------------------------------
diff --git 
a/nifi-api/src/main/java/org/apache/nifi/provenance/search/QuerySubmission.java 
b/nifi-api/src/main/java/org/apache/nifi/provenance/search/QuerySubmission.java
index 4716d2d..45efe4b 100644
--- 
a/nifi-api/src/main/java/org/apache/nifi/provenance/search/QuerySubmission.java
+++ 
b/nifi-api/src/main/java/org/apache/nifi/provenance/search/QuerySubmission.java
@@ -52,4 +52,9 @@ public interface QuerySubmission {
      * <code>false</code> otherwise
      */
     boolean isCanceled();
+
+    /**
+     * @return the identity of the user who submitted the query
+     */
+    String getSubmitterIdentity();
 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncLineageSubmission.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncLineageSubmission.java
 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncLineageSubmission.java
index 4a52a89..82bb542 100644
--- 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncLineageSubmission.java
+++ 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncLineageSubmission.java
@@ -34,19 +34,26 @@ public class AsyncLineageSubmission implements 
ComputeLineageSubmission {
     private final LineageComputationType computationType;
     private final Long eventId;
     private final Collection<String> lineageFlowFileUuids;
+    private final String submitterId;
 
     private volatile boolean canceled = false;
 
     private final StandardLineageResult result;
 
-    public AsyncLineageSubmission(final LineageComputationType 
computationType, final Long eventId, final Collection<String> 
lineageFlowFileUuids, final int numSteps) {
+    public AsyncLineageSubmission(final LineageComputationType 
computationType, final Long eventId, final Collection<String> 
lineageFlowFileUuids, final int numSteps, final String submitterId) {
         this.computationType = computationType;
         this.eventId = eventId;
         this.lineageFlowFileUuids = lineageFlowFileUuids;
+        this.submitterId = submitterId;
         this.result = new StandardLineageResult(numSteps, 
lineageFlowFileUuids);
     }
 
     @Override
+    public String getSubmitterIdentity() {
+        return submitterId;
+    }
+
+    @Override
     public StandardLineageResult getResult() {
         return result;
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncQuerySubmission.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncQuerySubmission.java
 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncQuerySubmission.java
index 00c6170..cd2ab39 100644
--- 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncQuerySubmission.java
+++ 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/AsyncQuerySubmission.java
@@ -31,6 +31,7 @@ public class AsyncQuerySubmission implements QuerySubmission {
 
     private volatile boolean canceled = false;
     private final StandardQueryResult queryResult;
+    private final String submitterId;
 
     /**
      * Constructs an AsyncQuerySubmission with the given query and the given
@@ -40,12 +41,18 @@ public class AsyncQuerySubmission implements 
QuerySubmission {
      * @param query the query to execute
      * @param numSteps how many steps to include
      */
-    public AsyncQuerySubmission(final Query query, final int numSteps) {
+    public AsyncQuerySubmission(final Query query, final int numSteps, final 
String submitterId) {
         this.query = query;
+        this.submitterId = submitterId;
         queryResult = new StandardQueryResult(query, numSteps);
     }
 
     @Override
+    public String getSubmitterIdentity() {
+        return submitterId;
+    }
+
+    @Override
     public Date getSubmissionTime() {
         return submissionTime;
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/PlaceholderProvenanceEvent.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/PlaceholderProvenanceEvent.java
 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/PlaceholderProvenanceEvent.java
new file mode 100644
index 0000000..bc40302
--- /dev/null
+++ 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/PlaceholderProvenanceEvent.java
@@ -0,0 +1,196 @@
+/*
+ * 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.provenance;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A Provenance Event that is used to replace another Provenance Event when 
authorizations
+ * are not granted for the original Provenance Event
+ */
+public class PlaceholderProvenanceEvent implements ProvenanceEventRecord {
+    private final String componentId;
+    private final long eventId;
+    private final long eventTime;
+    private final String flowFileUuid;
+
+    public PlaceholderProvenanceEvent(final ProvenanceEventRecord original) {
+        this.componentId = original.getComponentId();
+        this.eventId = original.getEventId();
+        this.eventTime = original.getEventTime();
+        this.flowFileUuid = original.getFlowFileUuid();
+    }
+
+    @Override
+    public long getEventId() {
+        return eventId;
+    }
+
+    @Override
+    public long getEventTime() {
+        return eventTime;
+    }
+
+    @Override
+    public long getFlowFileEntryDate() {
+        return 0;
+    }
+
+    @Override
+    public long getLineageStartDate() {
+        return 0;
+    }
+
+    @Override
+    public Set<String> getLineageIdentifiers() {
+        return Collections.emptySet();
+    }
+
+    @Override
+    public long getFileSize() {
+        return -1L;
+    }
+
+    @Override
+    public Long getPreviousFileSize() {
+        return -1L;
+    }
+
+    @Override
+    public long getEventDuration() {
+        return -1L;
+    }
+
+    @Override
+    public ProvenanceEventType getEventType() {
+        return ProvenanceEventType.UNKNOWN;
+    }
+
+    @Override
+    public Map<String, String> getAttributes() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<String, String> getPreviousAttributes() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Map<String, String> getUpdatedAttributes() {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public String getComponentId() {
+        return componentId;
+    }
+
+    @Override
+    public String getComponentType() {
+        return null;
+    }
+
+    @Override
+    public String getTransitUri() {
+        return null;
+    }
+
+    @Override
+    public String getSourceSystemFlowFileIdentifier() {
+        return null;
+    }
+
+    @Override
+    public String getFlowFileUuid() {
+        return flowFileUuid;
+    }
+
+    @Override
+    public List<String> getParentUuids() {
+        return null;
+    }
+
+    @Override
+    public List<String> getChildUuids() {
+        return null;
+    }
+
+    @Override
+    public String getAlternateIdentifierUri() {
+        return null;
+    }
+
+    @Override
+    public String getDetails() {
+        return null;
+    }
+
+    @Override
+    public String getRelationship() {
+        return null;
+    }
+
+    @Override
+    public String getSourceQueueIdentifier() {
+        return null;
+    }
+
+    @Override
+    public String getContentClaimSection() {
+        return null;
+    }
+
+    @Override
+    public String getPreviousContentClaimSection() {
+        return null;
+    }
+
+    @Override
+    public String getContentClaimContainer() {
+        return null;
+    }
+
+    @Override
+    public String getPreviousContentClaimContainer() {
+        return null;
+    }
+
+    @Override
+    public String getContentClaimIdentifier() {
+        return null;
+    }
+
+    @Override
+    public String getPreviousContentClaimIdentifier() {
+        return null;
+    }
+
+    @Override
+    public Long getContentClaimOffset() {
+        return null;
+    }
+
+    @Override
+    public Long getPreviousContentClaimOffset() {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardLineageResult.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardLineageResult.java
 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardLineageResult.java
index 02c089d..78b3188 100644
--- 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardLineageResult.java
+++ 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardLineageResult.java
@@ -268,7 +268,7 @@ public class StandardLineageResult implements 
ComputeLineageResult {
                             final boolean isNewFlowFile = nodes.add(childNode);
                             if (!isNewFlowFile) {
                                 final String msg = "Unable to generate Lineage 
Graph because multiple "
-                                        + "events were registered claiming to 
have generated the same FlowFile (UUID = " + childNode.getFlowFileUuid() + ")";
+                                    + "events were registered claiming to have 
generated the same FlowFile (UUID = " + childNode.getFlowFileUuid() + ")";
                                 logger.error(msg);
                                 setError(msg);
                                 return;
@@ -287,7 +287,7 @@ public class StandardLineageResult implements 
ComputeLineageResult {
                         lastEventMap.put(parentUuid, lineageNode);
                     }
                 }
-                break;
+                    break;
                 case RECEIVE:
                 case CREATE: {
                     // for a receive event, we want to create a FlowFile Node 
that represents the FlowFile received
@@ -296,7 +296,7 @@ public class StandardLineageResult implements 
ComputeLineageResult {
                     final boolean isNewFlowFile = nodes.add(flowFileNode);
                     if (!isNewFlowFile) {
                         final String msg = "Found cycle in graph. This 
indicates that multiple events "
-                                + "were registered claiming to have generated 
the same FlowFile (UUID = " + flowFileNode.getFlowFileUuid() + ")";
+                            + "were registered claiming to have generated the 
same FlowFile (UUID = " + flowFileNode.getFlowFileUuid() + ")";
                         setError(msg);
                         logger.error(msg);
                         return;
@@ -304,7 +304,7 @@ public class StandardLineageResult implements 
ComputeLineageResult {
                     edges.add(new EdgeNode(record.getFlowFileUuid(), 
lineageNode, flowFileNode));
                     lastEventMap.put(record.getFlowFileUuid(), flowFileNode);
                 }
-                break;
+                    break;
                 default:
                     break;
             }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardQueryResult.java
----------------------------------------------------------------------
diff --git 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardQueryResult.java
 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardQueryResult.java
index 03ab3ea..5c09e8e 100644
--- 
a/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardQueryResult.java
+++ 
b/nifi-commons/nifi-data-provenance-utils/src/main/java/org/apache/nifi/provenance/StandardQueryResult.java
@@ -90,7 +90,19 @@ public class StandardQueryResult implements QueryResult {
     public long getTotalHitCount() {
         readLock.lock();
         try {
-            return totalHitCount;
+            // Because we filter the results based on the user's permissions,
+            // we don't want to indicate that the total hit count is 1,000+ 
when we
+            // have 0 matching records, for instance. So, if we have fewer 
matching
+            // records than the max specified by the query, it is either the 
case that
+            // we truly don't have enough records to reach the max results, or 
that
+            // the user is not authorized to see some of the results. Either 
way,
+            // we want to report the number of events that we find AND that 
the user
+            // is allowed to see, so we report matching record count, or up to 
max results.
+            if (matchingRecords.size() < query.getMaxResults()) {
+                return matchingRecords.size();
+            } else {
+                return query.getMaxResults();
+            }
         } finally {
             readLock.unlock();
         }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-mock/src/main/java/org/apache/nifi/provenance/MockProvenanceEventRepository.java
----------------------------------------------------------------------
diff --git 
a/nifi-mock/src/main/java/org/apache/nifi/provenance/MockProvenanceEventRepository.java
 
b/nifi-mock/src/main/java/org/apache/nifi/provenance/MockProvenanceEventRepository.java
index 241041a..dc71ed5 100644
--- 
a/nifi-mock/src/main/java/org/apache/nifi/provenance/MockProvenanceEventRepository.java
+++ 
b/nifi-mock/src/main/java/org/apache/nifi/provenance/MockProvenanceEventRepository.java
@@ -22,6 +22,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.nifi.authorization.Authorizer;
+import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.events.EventReporter;
 import org.apache.nifi.provenance.lineage.ComputeLineageSubmission;
 import org.apache.nifi.provenance.search.Query;
@@ -54,7 +56,8 @@ public class MockProvenanceEventRepository implements 
ProvenanceEventRepository
     }
 
     @Override
-    public void initialize(EventReporter reporter) throws IOException {
+    public void initialize(EventReporter eventReporter, Authorizer authorizer, 
ProvenanceAuthorizableFactory resourceFactory) throws IOException {
+
     }
 
     @Override
@@ -67,32 +70,37 @@ public class MockProvenanceEventRepository implements 
ProvenanceEventRepository
     }
 
     @Override
+    public List<ProvenanceEventRecord> getEvents(long firstRecordId, int 
maxRecords, NiFiUser user) throws IOException {
+        return getEvents(firstRecordId, maxRecords);
+    }
+
+    @Override
     public Long getMaxEventId() {
         return Long.valueOf(records.size() - 1);
     }
 
     @Override
-    public QuerySubmission submitQuery(Query query) {
+    public QuerySubmission submitQuery(Query query, NiFiUser user) {
         throw new UnsupportedOperationException("MockProvenanceEventRepository 
does not support querying");
     }
 
     @Override
-    public QuerySubmission retrieveQuerySubmission(String queryIdentifier) {
+    public QuerySubmission retrieveQuerySubmission(String queryIdentifier, 
NiFiUser user) {
         throw new UnsupportedOperationException("MockProvenanceEventRepository 
does not support querying");
     }
 
     @Override
-    public ComputeLineageSubmission submitLineageComputation(String 
flowFileUuid) {
+    public ComputeLineageSubmission submitLineageComputation(String 
flowFileUuid, NiFiUser user) {
         throw new UnsupportedOperationException("MockProvenanceEventRepository 
does not support Lineage Computation");
     }
 
     @Override
-    public ComputeLineageSubmission retrieveLineageSubmission(String 
lineageIdentifier) {
+    public ComputeLineageSubmission retrieveLineageSubmission(String 
lineageIdentifier, NiFiUser user) {
         throw new UnsupportedOperationException("MockProvenanceEventRepository 
does not support Lineage Computation");
     }
 
     @Override
-    public ProvenanceEventRecord getEvent(long id) throws IOException {
+    public ProvenanceEventRecord getEvent(long id, NiFiUser user) throws 
IOException {
         if (id > records.size()) {
             return null;
         }
@@ -101,12 +109,12 @@ public class MockProvenanceEventRepository implements 
ProvenanceEventRepository
     }
 
     @Override
-    public ComputeLineageSubmission submitExpandParents(long eventId) {
+    public ComputeLineageSubmission submitExpandParents(long eventId, NiFiUser 
user) {
         throw new UnsupportedOperationException("MockProvenanceEventRepository 
does not support Lineage Computation");
     }
 
     @Override
-    public ComputeLineageSubmission submitExpandChildren(long eventId) {
+    public ComputeLineageSubmission submitExpandChildren(long eventId, 
NiFiUser user) {
         throw new UnsupportedOperationException("MockProvenanceEventRepository 
does not support Lineage Computation");
     }
 

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageDTO.java
index bc68d78..46d99bf 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageDTO.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageDTO.java
@@ -30,7 +30,6 @@ public class LineageDTO {
 
     private String id;
     private String uri;
-    private String clusterNodeId;
 
     private Date submissionTime;
     private Date expiration;
@@ -69,20 +68,6 @@ public class LineageDTO {
     }
 
     /**
-     * @return id of the node in the cluster where this lineage originated
-     */
-    @ApiModelProperty(
-            value = "The id of the node where this lineage originated if 
clustered."
-    )
-    public String getClusterNodeId() {
-        return clusterNodeId;
-    }
-
-    public void setClusterNodeId(String clusterNodeId) {
-        this.clusterNodeId = clusterNodeId;
-    }
-
-    /**
      * @return submission time for this lineage
      */
     @XmlJavaTypeAdapter(TimestampAdapter.class)

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageRequestDTO.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageRequestDTO.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageRequestDTO.java
index afab621..5a59a7c 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageRequestDTO.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-client-dto/src/main/java/org/apache/nifi/web/api/dto/provenance/lineage/LineageRequestDTO.java
@@ -41,6 +41,7 @@ public class LineageRequestDTO {
     private LineageRequestType lineageRequestType;
 
     private String uuid;
+    private String clusterNodeId;
 
     /**
      * @return event id that was used to generate this lineage
@@ -74,6 +75,18 @@ public class LineageRequestDTO {
     }
 
     /**
+     * @return id of the node in the cluster where this lineage originated
+     */
+    @ApiModelProperty(value = "The id of the node where this lineage 
originated if clustered.")
+    public String getClusterNodeId() {
+        return clusterNodeId;
+    }
+
+    public void setClusterNodeId(String clusterNodeId) {
+        this.clusterNodeId = clusterNodeId;
+    }
+
+    /**
      * @return uuid that was used to generate this lineage
      */
     @ApiModelProperty(

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
deleted file mode 100644
index 092e80c..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/AccessDeniedException.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.authorization;
-
-/**
- * Represents any error that might occur while authorizing user requests.
- */
-public class AccessDeniedException extends RuntimeException {
-
-    public AccessDeniedException(Throwable cause) {
-        super(cause);
-    }
-
-    public AccessDeniedException(String message, Throwable cause) {
-        super(message, cause);
-    }
-
-    public AccessDeniedException(String message) {
-        super(message);
-    }
-
-    public AccessDeniedException() {
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
deleted file mode 100644
index 50f7288..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/Authorizable.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.authorization.resource;
-
-import org.apache.nifi.authorization.AccessDeniedException;
-import org.apache.nifi.authorization.AuthorizationRequest;
-import org.apache.nifi.authorization.AuthorizationResult;
-import org.apache.nifi.authorization.AuthorizationResult.Result;
-import org.apache.nifi.authorization.Authorizer;
-import org.apache.nifi.authorization.RequestAction;
-import org.apache.nifi.authorization.Resource;
-import org.apache.nifi.authorization.user.NiFiUser;
-import org.apache.nifi.authorization.user.NiFiUserUtils;
-
-public interface Authorizable {
-
-    /**
-     * The parent for this Authorizable. May be null.
-     *
-     * @return the parent authorizable or null
-     */
-    Authorizable getParentAuthorizable();
-
-    /**
-     * The Resource for this Authorizable.
-     *
-     * @return the parent resource
-     */
-    Resource getResource();
-
-    /**
-     * Returns whether the current user is authorized for the specified action 
on the specified resource. This
-     * method does not imply the user is directly attempting to access the 
specified resource. If the user is
-     * attempting a direct access use Authorizable.authorize().
-     *
-     * @param authorizer authorizer
-     * @param action action
-     * @return is authorized
-     */
-    default boolean isAuthorized(Authorizer authorizer, RequestAction action) {
-        return Result.Approved.equals(checkAuthorization(authorizer, 
action).getResult());
-    }
-
-    /**
-     * Returns the result of an authorization request for the current user for 
the specified action on the specified
-     * resource. This method does not imply the user is directly attempting to 
access the specified resource. If the user is
-     * attempting a direct access use Authorizable.authorize().
-     *
-     * @param authorizer authorizer
-     * @param action action
-     * @return is authorized
-     */
-    default AuthorizationResult checkAuthorization(Authorizer authorizer, 
RequestAction action) {
-        return checkAuthorization(authorizer, action, 
NiFiUserUtils.getNiFiUser());
-    }
-
-    /**
-     * Returns the result of an authorization request for the specified user 
for the specified action on the specified
-     * resource. This method does not imply the user is directly attempting to 
access the specified resource. If the user is
-     * attempting a direct access use Authorizable.authorize().
-     *
-     * @param authorizer authorizer
-     * @param action action
-     * @param user user
-     * @return is authorized
-     */
-    default AuthorizationResult checkAuthorization(Authorizer authorizer, 
RequestAction action, NiFiUser user) {
-        // TODO - include user details context
-
-        // build the request
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-                .identity(user.getIdentity())
-                .anonymous(user.isAnonymous())
-                .accessAttempt(false)
-                .action(action)
-                .resource(getResource())
-                .build();
-
-        // perform the authorization
-        final AuthorizationResult result = authorizer.authorize(request);
-
-        // verify the results
-        if (Result.ResourceNotFound.equals(result.getResult())) {
-            final Authorizable parent = getParentAuthorizable();
-            if (parent == null) {
-                return AuthorizationResult.denied();
-            } else {
-                return parent.checkAuthorization(authorizer, action);
-            }
-        } else {
-            return result;
-        }
-    }
-
-    /**
-     * Authorizes the current user for the specified action on the specified 
resource. This method does imply the user is
-     * directly accessing the specified resource.
-     *
-     * @param authorizer authorizer
-     * @param action action
-     */
-    default void authorize(Authorizer authorizer, RequestAction action) throws 
AccessDeniedException {
-        final NiFiUser user = NiFiUserUtils.getNiFiUser();
-
-        // TODO - include user details context
-
-        final AuthorizationRequest request = new AuthorizationRequest.Builder()
-            .identity(user.getIdentity())
-            .anonymous(user.isAnonymous())
-            .accessAttempt(true)
-            .action(action)
-            .resource(getResource())
-            .build();
-
-        final AuthorizationResult result = authorizer.authorize(request);
-        if (Result.ResourceNotFound.equals(result.getResult())) {
-            final Authorizable parent = getParentAuthorizable();
-            if (parent == null) {
-                throw new AccessDeniedException("Access is denied");
-            } else {
-                parent.authorize(authorizer, action);
-            }
-        } else if (Result.Denied.equals(result.getResult())) {
-            throw new AccessDeniedException(result.getExplanation());
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ProvenanceEventAuthorizable.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ProvenanceEventAuthorizable.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ProvenanceEventAuthorizable.java
new file mode 100644
index 0000000..153047e
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ProvenanceEventAuthorizable.java
@@ -0,0 +1,41 @@
+/*
+ * 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.authorization.resource;
+
+import org.apache.nifi.authorization.Resource;
+
+public class ProvenanceEventAuthorizable implements Authorizable {
+    final Authorizable authorizable;
+
+    public ProvenanceEventAuthorizable(final Authorizable authorizable) {
+        this.authorizable = authorizable;
+    }
+
+    @Override
+    public Authorizable getParentAuthorizable() {
+        if (authorizable.getParentAuthorizable() == null) {
+            return null;
+        } else {
+            return new 
ProvenanceEventAuthorizable(authorizable.getParentAuthorizable());
+        }
+    }
+
+    @Override
+    public Resource getResource() {
+        return 
ResourceFactory.getProvenanceEventResource(authorizable.getResource());
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
index 5fc56af..058a1c8 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceFactory.java
@@ -179,6 +179,18 @@ public final class ResourceFactory {
         }
     };
 
+    private final static Resource PROVENANCE_EVENT_RESOURCE = new Resource() {
+        @Override
+        public String getIdentifier() {
+            return ResourceType.ProvenanceEvent.getValue();
+        }
+
+        @Override
+        public String getName() {
+            return "Provenance Event";
+        }
+    };
+
     private final static Resource PROXY_RESOURCE = new Resource() {
         @Override
         public String getIdentifier() {
@@ -581,22 +593,19 @@ public final class ResourceFactory {
     /**
      * Gets a Resource for accessing a component's provenance events.
      *
-     * @param resourceType  The type of resource being accessed
-     * @param identifier    The identifier of the component being accessed
-     * @param name          The name of the component being accessed
-     * @return              The resource
+     * @param resource The resource for the component being accessed
+     * @return The resource for the provenance of the component being accessed
      */
-    public static Resource getComponentProvenanceResource(final ResourceType 
resourceType, final String identifier, final String name) {
-        final Resource componentResource = getComponentResource(resourceType, 
identifier, name);
+    public static Resource getProvenanceEventResource(final Resource resource) 
{
         return new Resource() {
             @Override
             public String getIdentifier() {
-                return String.format("%s/%s", 
componentResource.getIdentifier(), "provenance");
+                return String.format("%s%s", 
PROVENANCE_EVENT_RESOURCE.getIdentifier(), resource.getIdentifier());
             }
 
             @Override
             public String getName() {
-                return componentResource.getName() + " provenance";
+                return "Provenance Events for " + resource.getName();
             }
         };
     }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java
index eb08cec..1464cab 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/ResourceType.java
@@ -30,6 +30,7 @@ public enum ResourceType {
     Processor("/processors"),
     ProcessGroup("/process-groups"),
     Provenance("/provenance"),
+    ProvenanceEvent("/provenance-events"),
     Proxy("/proxy"),
     RemoteProcessGroup("/remote-process-groups"),
     ReportingTask("/reporting-tasks"),

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
deleted file mode 100644
index f560cd7..0000000
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/NiFiUser.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * 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.authorization.user;
-
-import java.io.Serializable;
-import java.util.Objects;
-
-/**
- * An NiFiUser.
- */
-public class NiFiUser implements Serializable {
-
-    public static final NiFiUser ANONYMOUS = new NiFiUser("anonymous");
-
-    private String identity;
-    private String userName;
-
-    private NiFiUser chain;
-
-    public NiFiUser(String identity) {
-        this(identity, identity, null);
-    }
-
-    public NiFiUser(String identity, NiFiUser chain) {
-        this(identity, identity, chain);
-    }
-
-    public NiFiUser(String identity, String userName, NiFiUser chain) {
-        this.identity = identity;
-        this.userName = userName;
-        this.chain = chain;
-    }
-
-    /* getters / setters */
-
-    public String getIdentity() {
-        return identity;
-    }
-
-    public String getUserName() {
-        return userName;
-    }
-
-    public NiFiUser getChain() {
-        return chain;
-    }
-
-    public boolean isAnonymous() {
-        return this == ANONYMOUS;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        final NiFiUser other = (NiFiUser) obj;
-        if (!Objects.equals(this.identity, other.identity)) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int hash = 7;
-        hash = 53 * hash + Objects.hashCode(this.identity);
-        return hash;
-    }
-
-    @Override
-    public String toString() {
-        return String.format("identity[%s], userName[%s]", getIdentity(), 
getUserName(), ", ");
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java
new file mode 100644
index 0000000..8c41a1f
--- /dev/null
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/user/StandardNiFiUser.java
@@ -0,0 +1,94 @@
+/*
+ * 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.authorization.user;
+
+import java.io.Serializable;
+import java.util.Objects;
+
+/**
+ * An implementation of NiFiUser.
+ */
+public class StandardNiFiUser implements NiFiUser, Serializable {
+    private static final long serialVersionUID = -5503790026187817496L;
+
+    public static final StandardNiFiUser ANONYMOUS = new 
StandardNiFiUser("anonymous");
+
+    private final String identity;
+    private final String userName;
+    private final NiFiUser chain;
+
+    public StandardNiFiUser(String identity) {
+        this(identity, identity, null);
+    }
+
+    public StandardNiFiUser(String identity, NiFiUser chain) {
+        this(identity, identity, chain);
+    }
+
+    public StandardNiFiUser(String identity, String userName, NiFiUser chain) {
+        this.identity = identity;
+        this.userName = userName;
+        this.chain = chain;
+    }
+
+
+    @Override
+    public String getIdentity() {
+        return identity;
+    }
+
+    @Override
+    public String getUserName() {
+        return userName;
+    }
+
+    @Override
+    public NiFiUser getChain() {
+        return chain;
+    }
+
+    @Override
+    public boolean isAnonymous() {
+        return this == ANONYMOUS;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+
+        if (!(obj instanceof NiFiUser)) {
+            return false;
+        }
+
+        final NiFiUser other = (NiFiUser) obj;
+        return Objects.equals(this.identity, other.getIdentity());
+    }
+
+    @Override
+    public int hashCode() {
+        int hash = 7;
+        hash = 53 * hash + Objects.hashCode(this.identity);
+        return hash;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("identity[%s], userName[%s]", getIdentity(), 
getUserName(), ", ");
+    }
+}

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java
index b9fd0cb..e50fe39 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/Template.java
@@ -30,7 +30,6 @@ import org.apache.nifi.authorization.resource.Authorizable;
 import org.apache.nifi.authorization.resource.ResourceFactory;
 import org.apache.nifi.authorization.resource.ResourceType;
 import org.apache.nifi.authorization.user.NiFiUser;
-import org.apache.nifi.authorization.user.NiFiUserUtils;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.controller.label.Label;
 import org.apache.nifi.groups.ProcessGroup;
@@ -139,8 +138,8 @@ public class Template implements Authorizable {
     }
 
     @Override
-    public void authorize(final Authorizer authorizer, final RequestAction 
action) throws AccessDeniedException {
-        final AuthorizationResult result = checkAuthorization(authorizer, 
action, true);
+    public void authorize(final Authorizer authorizer, final RequestAction 
action, final NiFiUser user) throws AccessDeniedException {
+        final AuthorizationResult result = checkAuthorization(authorizer, 
action, true, user);
         if (Result.Denied.equals(result)) {
             final String explanation = result.getExplanation() == null ? 
"Access is denied" : result.getExplanation();
             throw new AccessDeniedException(explanation);
@@ -148,13 +147,11 @@ public class Template implements Authorizable {
     }
 
     @Override
-    public AuthorizationResult checkAuthorization(final Authorizer authorizer, 
final RequestAction action) {
-        return checkAuthorization(authorizer, action, false);
+    public AuthorizationResult checkAuthorization(final Authorizer authorizer, 
final RequestAction action, final NiFiUser user) {
+        return checkAuthorization(authorizer, action, false, user);
     }
 
-    private AuthorizationResult checkAuthorization(final Authorizer 
authorizer, final RequestAction action, final boolean accessAttempt) {
-        final NiFiUser user = NiFiUserUtils.getNiFiUser();
-
+    private AuthorizationResult checkAuthorization(final Authorizer 
authorizer, final RequestAction action, final boolean accessAttempt, final 
NiFiUser user) {
         // TODO - include user details context
 
         // build the request
@@ -172,7 +169,7 @@ public class Template implements Authorizable {
         // verify the results
         if (Result.ResourceNotFound.equals(result.getResult())) {
             for (final Authorizable child : getAuthorizableComponents()) {
-                final AuthorizationResult childResult = 
child.checkAuthorization(authorizer, action);
+                final AuthorizationResult childResult = 
child.checkAuthorization(authorizer, action, user);
                 if (Result.Denied.equals(childResult)) {
                     return childResult;
                 }

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
index c68c734..3d2eca2 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/FlowController.java
@@ -16,7 +16,39 @@
  */
 package org.apache.nifi.controller;
 
-import com.sun.jersey.api.client.ClientHandlerException;
+import static java.util.Objects.requireNonNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.LockSupport;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import javax.net.ssl.SSLContext;
+
 import org.apache.commons.lang3.StringUtils;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.admin.service.AuditService;
@@ -29,7 +61,9 @@ import 
org.apache.nifi.annotation.notification.PrimaryNodeState;
 import org.apache.nifi.authorization.Authorizer;
 import org.apache.nifi.authorization.Resource;
 import org.apache.nifi.authorization.resource.Authorizable;
+import org.apache.nifi.authorization.resource.ProvenanceEventAuthorizable;
 import org.apache.nifi.authorization.resource.ResourceFactory;
+import org.apache.nifi.authorization.user.NiFiUser;
 import org.apache.nifi.cluster.HeartbeatPayload;
 import org.apache.nifi.cluster.coordination.heartbeat.HeartbeatMonitor;
 import org.apache.nifi.cluster.coordination.node.ClusterRoles;
@@ -151,6 +185,7 @@ import 
org.apache.nifi.processor.StandardValidationContextFactory;
 import org.apache.nifi.provenance.ProvenanceEventRecord;
 import org.apache.nifi.provenance.ProvenanceEventRepository;
 import org.apache.nifi.provenance.ProvenanceEventType;
+import org.apache.nifi.provenance.ProvenanceAuthorizableFactory;
 import org.apache.nifi.provenance.StandardProvenanceEventRecord;
 import org.apache.nifi.remote.HttpRemoteSiteListener;
 import org.apache.nifi.remote.RemoteGroupPort;
@@ -178,6 +213,7 @@ import org.apache.nifi.stream.io.StreamUtils;
 import org.apache.nifi.util.FormatUtils;
 import org.apache.nifi.util.NiFiProperties;
 import org.apache.nifi.util.ReflectionUtils;
+import org.apache.nifi.web.ResourceNotFoundException;
 import org.apache.nifi.web.api.dto.ConnectableDTO;
 import org.apache.nifi.web.api.dto.ConnectionDTO;
 import org.apache.nifi.web.api.dto.ControllerServiceDTO;
@@ -198,39 +234,9 @@ import 
org.apache.zookeeper.server.quorum.QuorumPeerConfig.ConfigException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.net.ssl.SSLContext;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.LockSupport;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import static java.util.Objects.requireNonNull;
+import com.sun.jersey.api.client.ClientHandlerException;
 
-public class FlowController implements EventAccess, ControllerServiceProvider, 
ReportingTaskProvider, QueueProvider, Authorizable {
+public class FlowController implements EventAccess, ControllerServiceProvider, 
ReportingTaskProvider, QueueProvider, Authorizable, 
ProvenanceAuthorizableFactory {
 
     // default repository implementations
     public static final String DEFAULT_FLOWFILE_REPO_IMPLEMENTATION = 
"org.apache.nifi.controller.repository.WriteAheadFlowFileRepository";
@@ -437,7 +443,7 @@ public class FlowController implements EventAccess, 
ControllerServiceProvider, R
 
         try {
             this.provenanceEventRepository = 
createProvenanceRepository(properties);
-            
this.provenanceEventRepository.initialize(createEventReporter(bulletinRepository));
+            
this.provenanceEventRepository.initialize(createEventReporter(bulletinRepository),
 authorizer, this);
         } catch (final Exception e) {
             throw new RuntimeException("Unable to create Provenance 
Repository", e);
         }
@@ -3575,17 +3581,17 @@ public class FlowController implements EventAccess, 
ControllerServiceProvider, R
         return null;
     }
 
-    public ProvenanceEventRecord replayFlowFile(final long 
provenanceEventRecordId, final String requestor) throws IOException {
-        final ProvenanceEventRecord record = 
provenanceEventRepository.getEvent(provenanceEventRecordId);
+    public ProvenanceEventRecord replayFlowFile(final long 
provenanceEventRecordId, final NiFiUser user) throws IOException {
+        final ProvenanceEventRecord record = 
provenanceEventRepository.getEvent(provenanceEventRecordId, user);
         if (record == null) {
             throw new IllegalStateException("Cannot find Provenance Event with 
ID " + provenanceEventRecordId);
         }
 
-        return replayFlowFile(record, requestor);
+        return replayFlowFile(record, user);
     }
 
     @SuppressWarnings("deprecation")
-    public ProvenanceEventRecord replayFlowFile(final ProvenanceEventRecord 
event, final String requestor) throws IOException {
+    public ProvenanceEventRecord replayFlowFile(final ProvenanceEventRecord 
event, final NiFiUser user) throws IOException {
         if (event == null) {
             throw new NullPointerException();
         }
@@ -3684,7 +3690,7 @@ public class FlowController implements EventAccess, 
ControllerServiceProvider, R
             .setFlowFileUUID(parentUUID)
             .setAttributes(Collections.<String, String> emptyMap(), 
flowFileRecord.getAttributes())
             .setCurrentContentClaim(event.getContentClaimContainer(), 
event.getContentClaimSection(), event.getContentClaimIdentifier(), 
event.getContentClaimOffset(), event.getFileSize())
-            .setDetails("Replay requested by " + requestor)
+            .setDetails("Replay requested by " + user.getIdentity())
             .setEventTime(System.currentTimeMillis())
             .setFlowFileEntryDate(System.currentTimeMillis())
             .setLineageStartDate(event.getLineageStartDate())
@@ -3847,6 +3853,29 @@ public class FlowController implements EventAccess, 
ControllerServiceProvider, R
     }
 
     @Override
+    public Authorizable createProvenanceAuthorizable(final String componentId) 
{
+        final String rootGroupId = getRootGroupId();
+
+        // Provenance Events are generated only by connectable components, 
with the exception of DOWNLOAD events,
+        // which have the root process group's identifier assigned as the 
component ID. So, we check if the component ID
+        // is set to the root group and otherwise assume that the ID is that 
of a component.
+        final ProvenanceEventAuthorizable authorizable;
+        if (rootGroupId.equals(componentId)) {
+            authorizable = new ProvenanceEventAuthorizable(rootGroup);
+        } else {
+            final Connectable connectable = 
rootGroup.findConnectable(componentId);
+
+            if (connectable == null) {
+                throw new ResourceNotFoundException("The component that 
generated this event is no longer part of the data flow.");
+            }
+
+            authorizable = new ProvenanceEventAuthorizable(connectable);
+        }
+
+        return authorizable;
+    }
+
+    @Override
     public List<Action> getFlowChanges(final int firstActionId, final int 
maxActions) {
         final History history = auditService.getActions(firstActionId, 
maxActions);
         return new ArrayList<>(history.getActions());

http://git-wip-us.apache.org/repos/asf/nifi/blob/ae9e2fdf/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
----------------------------------------------------------------------
diff --git 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
index eb8ea20..180b6bc 100644
--- 
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
+++ 
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java
@@ -2812,7 +2812,7 @@ public class StandardNiFiServiceFacade implements 
NiFiServiceFacade {
         }
 
         // perform the authorization
-        final AuthorizationResult result = 
authorizable.checkAuthorization(authorizer, RequestAction.READ);
+        final AuthorizationResult result = 
authorizable.checkAuthorization(authorizer, RequestAction.READ, 
NiFiUserUtils.getNiFiUser());
         return Result.Approved.equals(result.getResult());
     }
 

Reply via email to