[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-19 Thread asfgit
Github user asfgit closed the pull request at:

https://github.com/apache/nifi/pull/2990


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-19 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r218682791
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
 ---
@@ -1060,7 +1069,8 @@ public ProcessGroupStatusDTO 
createProcessGroupStatusDto(final ProcessGroup proc
 final Collection 
childRemoteProcessGroupStatusCollection = 
processGroupStatus.getRemoteProcessGroupStatus();
 if (childRemoteProcessGroupStatusCollection != null) {
 for (final RemoteProcessGroupStatus 
childRemoteProcessGroupStatus : childRemoteProcessGroupStatusCollection) {
-final RemoteProcessGroupStatusDTO 
childRemoteProcessGroupStatusDto = 
createRemoteProcessGroupStatusDto(childRemoteProcessGroupStatus);
+final RemoteProcessGroup childRemoteProcessGroup = 
processGroup.getRemoteProcessGroup(childRemoteProcessGroupStatus.getId());
--- End diff --

Thanks for catching this. FIxed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-18 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r218531396
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/dto/DtoFactory.java
 ---
@@ -1060,7 +1069,8 @@ public ProcessGroupStatusDTO 
createProcessGroupStatusDto(final ProcessGroup proc
 final Collection 
childRemoteProcessGroupStatusCollection = 
processGroupStatus.getRemoteProcessGroupStatus();
 if (childRemoteProcessGroupStatusCollection != null) {
 for (final RemoteProcessGroupStatus 
childRemoteProcessGroupStatus : childRemoteProcessGroupStatusCollection) {
-final RemoteProcessGroupStatusDTO 
childRemoteProcessGroupStatusDto = 
createRemoteProcessGroupStatusDto(childRemoteProcessGroupStatus);
+final RemoteProcessGroup childRemoteProcessGroup = 
processGroup.getRemoteProcessGroup(childRemoteProcessGroupStatus.getId());
--- End diff --

The way this method is called recursively the `childRemoteProcessGroup` is 
not necessarily a child of `processGroup`. This can be seen by creating a 
`RemoteProcessGroup` in a nested `ProcessGroup`. Then open the Summary table. 
You'll see an NPE.

I think the fix is pretty trivial. We are actually already getting the 
`remoteProcessGroup` one line below using the recursive `findXxx` method. 
Moving this line up and using identifier from the status and not the DTO should 
address the problem.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-17 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r218272926
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
 ---
@@ -1045,29 +1041,38 @@
 var reportingTaskActionFormatter = function (row, cell, value, 
columnDef, dataContext) {
 var markup = '';
 
-if (dataContext.permissions.canRead && 
dataContext.permissions.canWrite) {
-if (dataContext.component.state === 'RUNNING') {
+var canWrite = dataContext.permissions.canWrite;
+var canRead = dataContext.permissions.canRead;
+var canOperate = dataContext.operatePermissions.canWrite || 
canWrite;
+var isStopped = dataContext.status.runStatus === 'STOPPED';
+
+if (dataContext.status.runStatus === 'RUNNING') {
+if (canOperate) {
 markup += '';
-} else if (dataContext.component.state === 'STOPPED' || 
dataContext.component.state === 'DISABLED') {
-markup += '';
+}
 
-// support starting when stopped and no validation 
errors
-if (dataContext.component.state === 'STOPPED' && 
nfCommon.isEmpty(dataContext.component.validationErrors)) {
-markup += '';
-}
+} else if (isStopped || dataContext.status.runStatus === 
'DISABLED') {
 
-if (dataContext.component.multipleVersionsAvailable 
=== true) {
-markup += '';
-}
+if (canRead && canWrite) {
+markup += '';
+}
 
-if (nfCommon.canModifyController()) {
-markup += '';
-}
+// support starting when stopped and no validation errors
+if (canOperate && dataContext.status.runStatus === 
'STOPPED' && dataContext.status.validationStatus === 'VALID') {
+markup += '';
 }
 
-if (dataContext.component.persistsState === true) {
-markup += '';
+if (canRead && canWrite && 
dataContext.component.multipleVersionsAvailable === true) {
+markup += '';
 }
+
+if (nfCommon.canModifyController()) {
--- End diff --

Thanks @mcgilman , you are right. I accidentally removed the conditions to 
check if reporting tasks and controller services can be read and write for 
delete icon. Thanks for catching this. Updated.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-14 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217816900
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
 ---
@@ -1045,29 +1041,38 @@
 var reportingTaskActionFormatter = function (row, cell, value, 
columnDef, dataContext) {
 var markup = '';
 
-if (dataContext.permissions.canRead && 
dataContext.permissions.canWrite) {
-if (dataContext.component.state === 'RUNNING') {
+var canWrite = dataContext.permissions.canWrite;
+var canRead = dataContext.permissions.canRead;
+var canOperate = dataContext.operatePermissions.canWrite || 
canWrite;
+var isStopped = dataContext.status.runStatus === 'STOPPED';
+
+if (dataContext.status.runStatus === 'RUNNING') {
+if (canOperate) {
 markup += '';
-} else if (dataContext.component.state === 'STOPPED' || 
dataContext.component.state === 'DISABLED') {
-markup += '';
+}
 
-// support starting when stopped and no validation 
errors
-if (dataContext.component.state === 'STOPPED' && 
nfCommon.isEmpty(dataContext.component.validationErrors)) {
-markup += '';
-}
+} else if (isStopped || dataContext.status.runStatus === 
'DISABLED') {
 
-if (dataContext.component.multipleVersionsAvailable 
=== true) {
-markup += '';
-}
+if (canRead && canWrite) {
+markup += '';
+}
 
-if (nfCommon.canModifyController()) {
-markup += '';
-}
+// support starting when stopped and no validation errors
+if (canOperate && dataContext.status.runStatus === 
'STOPPED' && dataContext.status.validationStatus === 'VALID') {
+markup += '';
 }
 
-if (dataContext.component.persistsState === true) {
-markup += '';
+if (canRead && canWrite && 
dataContext.component.multipleVersionsAvailable === true) {
+markup += '';
 }
+
+if (nfCommon.canModifyController()) {
--- End diff --

Ah, I see what's changed. I was a little confused. The issue is that 
component removal requires permissions to `WRITE` both the component in 
question and it's parent. With the changes introduced here, we are no longer 
including `canWrite` when determining the visibility of the delete icons.

When I was reviewing, I was seeing the delete icon when I shouldn't have 
been. I only had read and operate permissions to the component but the UI was 
presenting the delete icon (because I still had write permissions to the 
parent). However, when I attempted to actually delete it NiFi correctly told me 
I wasn't allowed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217605699
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
 ---
@@ -1045,29 +1041,38 @@
 var reportingTaskActionFormatter = function (row, cell, value, 
columnDef, dataContext) {
 var markup = '';
 
-if (dataContext.permissions.canRead && 
dataContext.permissions.canWrite) {
-if (dataContext.component.state === 'RUNNING') {
+var canWrite = dataContext.permissions.canWrite;
+var canRead = dataContext.permissions.canRead;
+var canOperate = dataContext.operatePermissions.canWrite || 
canWrite;
+var isStopped = dataContext.status.runStatus === 'STOPPED';
+
+if (dataContext.status.runStatus === 'RUNNING') {
+if (canOperate) {
 markup += '';
-} else if (dataContext.component.state === 'STOPPED' || 
dataContext.component.state === 'DISABLED') {
-markup += '';
+}
 
-// support starting when stopped and no validation 
errors
-if (dataContext.component.state === 'STOPPED' && 
nfCommon.isEmpty(dataContext.component.validationErrors)) {
-markup += '';
-}
+} else if (isStopped || dataContext.status.runStatus === 
'DISABLED') {
 
-if (dataContext.component.multipleVersionsAvailable 
=== true) {
-markup += '';
-}
+if (canRead && canWrite) {
+markup += '';
+}
 
-if (nfCommon.canModifyController()) {
-markup += '';
-}
+// support starting when stopped and no validation errors
+if (canOperate && dataContext.status.runStatus === 
'STOPPED' && dataContext.status.validationStatus === 'VALID') {
+markup += '';
 }
 
-if (dataContext.component.persistsState === true) {
-markup += '';
+if (canRead && canWrite && 
dataContext.component.multipleVersionsAvailable === true) {
+markup += '';
 }
+
+if (nfCommon.canModifyController()) {
--- End diff --

Is that so? I didn't change the code since I don't fully agree with the 
suggestion.

> The controller permission will have already been considered when 
determining a value for canWrite server-side since it should be the parent 
resource for every reporting task.

If the reporting task doesn't have its own policy setting, the above 
assumption would be true. However, if a policy for the reporting task is 
defined, the parent controller permission will not be taken into account the 
`canWrite`. The user can have WRITE to a reporting task without having WRITE to 
the controller. In that case, we still need to check controller's permission.

Also, I can see the similar restriction at nf-controller-services.js at 
line 962, checking only parent WRITE permission:
```
if (isDisabled && canWriteControllerServiceParent(dataContext)) {
markup += '';
}
```

Current conditions are consistent at both controller services and reporting 
tasks. I think it is not an issue. How do you think?



---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217604473
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
 ---
@@ -741,8 +740,8 @@
 })
 .attr('font-family', function (d) {
 var family = '';
-if (d.permissions.canRead) {
-if (hasIssues(d) || d.component.transmitting) {
+if (d.permissions.canRead || 
d.operatePermissions.canWrite) {
--- End diff --

That's true. I've removed permission checks for status icon.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217598519
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroupInputPort(id, 
createPortDTOWithDesiredRunStatus(portId, id, 
requestRemotePortRunStatusEntity)),
+(revision, remotePortRunStatusEntity) -> {
+// update the specified remote process group
+final RemoteProcessGroupPortEntity controllerResponse 
= serviceFacade.updateRemoteProcessGroupInputPort(revision, id,
+createPortDTOWithDesiredRunStatus(portId, id, 
remotePortRunStatusEntity));
+
+// get the 

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217598491
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
--- End diff --

Thank you very much for finding this! Fixed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217598443
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/OperationAuthorizable.java
 ---
@@ -0,0 +1,85 @@
+/*
+ * 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.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Authorizable for a component that can be scheduled by operators.
+ */
+public class OperationAuthorizable implements Authorizable, 
EnforcePolicyPermissionsThroughBaseResource {
+private static Logger logger = 
LoggerFactory.getLogger(OperationAuthorizable.class);
+private final Authorizable baseAuthorizable;
+
+public OperationAuthorizable(final Authorizable baseAuthorizable) {
+this.baseAuthorizable = baseAuthorizable;
+}
+
+@Override
+public Authorizable getParentAuthorizable() {
+// Need to return parent operation authorizable. E.g. 
/operation/processor/ -> /operation/process-group/ -> 
/run-status/process-group/root
+if (baseAuthorizable.getParentAuthorizable() == null) {
+return null;
+} else {
+return new 
OperationAuthorizable(baseAuthorizable.getParentAuthorizable());
+}
+}
+
+@Override
+public Authorizable getBaseAuthorizable() {
+return baseAuthorizable;
+}
+
+@Override
+public Resource getResource() {
+return 
ResourceFactory.getOperationResource(baseAuthorizable.getResource());
+}
+
+/**
+ * Authorize the request action with the resource using base 
authorizable and operation authorizable combination.
+ *
+ * This method authorizes the request with the base authorizable 
first. If the request is allowed, then finish authorization.
+ * If base authorizable denies the request, then it checks if the user 
has WRITE permission for '/operation/{componentType}/{id}'.
+ */
+public static void authorize(final Authorizable baseAuthorizable, 
final Authorizer authorizer, final RequestAction requestAction, final NiFiUser 
user) {
+try {
+baseAuthorizable.authorize(authorizer, requestAction, user);
+} catch (AccessDeniedException e) {
+logger.debug("Authorization failed with {}. Try authorizing 
with OperationAuthorizable.", baseAuthorizable, e);
+// Always use WRITE action for operation.
+new 
OperationAuthorizable(baseAuthorizable).authorize(authorizer, 
RequestAction.WRITE, user);
+}
+
+}
+
+/**
+ * Check if the request is authorized.
+ *
+ * @return True if the request is allowed by the base authorizable, or 
the user has WRITE permission for '/operation/{componentType}/id'.
+ */
+public static boolean isAuthorized(final Authorizable 
baseAuthorizable, final Authorizer authorizer, final RequestAction 
requestAction, final NiFiUser user) {
--- End diff --

Renamed this one, too.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217598426
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/OperationAuthorizable.java
 ---
@@ -0,0 +1,85 @@
+/*
+ * 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.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Authorizable for a component that can be scheduled by operators.
+ */
+public class OperationAuthorizable implements Authorizable, 
EnforcePolicyPermissionsThroughBaseResource {
+private static Logger logger = 
LoggerFactory.getLogger(OperationAuthorizable.class);
+private final Authorizable baseAuthorizable;
+
+public OperationAuthorizable(final Authorizable baseAuthorizable) {
+this.baseAuthorizable = baseAuthorizable;
+}
+
+@Override
+public Authorizable getParentAuthorizable() {
+// Need to return parent operation authorizable. E.g. 
/operation/processor/ -> /operation/process-group/ -> 
/run-status/process-group/root
+if (baseAuthorizable.getParentAuthorizable() == null) {
+return null;
+} else {
+return new 
OperationAuthorizable(baseAuthorizable.getParentAuthorizable());
+}
+}
+
+@Override
+public Authorizable getBaseAuthorizable() {
+return baseAuthorizable;
+}
+
+@Override
+public Resource getResource() {
+return 
ResourceFactory.getOperationResource(baseAuthorizable.getResource());
+}
+
+/**
+ * Authorize the request action with the resource using base 
authorizable and operation authorizable combination.
+ *
+ * This method authorizes the request with the base authorizable 
first. If the request is allowed, then finish authorization.
+ * If base authorizable denies the request, then it checks if the user 
has WRITE permission for '/operation/{componentType}/{id}'.
+ */
+public static void authorize(final Authorizable baseAuthorizable, 
final Authorizer authorizer, final RequestAction requestAction, final NiFiUser 
user) {
--- End diff --

I agree, that's a good idea. I renamed it.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217390883
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/OperationAuthorizable.java
 ---
@@ -0,0 +1,85 @@
+/*
+ * 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.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Authorizable for a component that can be scheduled by operators.
+ */
+public class OperationAuthorizable implements Authorizable, 
EnforcePolicyPermissionsThroughBaseResource {
+private static Logger logger = 
LoggerFactory.getLogger(OperationAuthorizable.class);
+private final Authorizable baseAuthorizable;
+
+public OperationAuthorizable(final Authorizable baseAuthorizable) {
+this.baseAuthorizable = baseAuthorizable;
+}
+
+@Override
+public Authorizable getParentAuthorizable() {
+// Need to return parent operation authorizable. E.g. 
/operation/processor/ -> /operation/process-group/ -> 
/run-status/process-group/root
+if (baseAuthorizable.getParentAuthorizable() == null) {
+return null;
+} else {
+return new 
OperationAuthorizable(baseAuthorizable.getParentAuthorizable());
+}
+}
+
+@Override
+public Authorizable getBaseAuthorizable() {
+return baseAuthorizable;
+}
+
+@Override
+public Resource getResource() {
+return 
ResourceFactory.getOperationResource(baseAuthorizable.getResource());
+}
+
+/**
+ * Authorize the request action with the resource using base 
authorizable and operation authorizable combination.
+ *
+ * This method authorizes the request with the base authorizable 
first. If the request is allowed, then finish authorization.
+ * If base authorizable denies the request, then it checks if the user 
has WRITE permission for '/operation/{componentType}/{id}'.
+ */
+public static void authorize(final Authorizable baseAuthorizable, 
final Authorizer authorizer, final RequestAction requestAction, final NiFiUser 
user) {
+try {
+baseAuthorizable.authorize(authorizer, requestAction, user);
+} catch (AccessDeniedException e) {
+logger.debug("Authorization failed with {}. Try authorizing 
with OperationAuthorizable.", baseAuthorizable, e);
+// Always use WRITE action for operation.
+new 
OperationAuthorizable(baseAuthorizable).authorize(authorizer, 
RequestAction.WRITE, user);
+}
+
+}
+
+/**
+ * Check if the request is authorized.
+ *
+ * @return True if the request is allowed by the base authorizable, or 
the user has WRITE permission for '/operation/{componentType}/id'.
+ */
+public static boolean isAuthorized(final Authorizable 
baseAuthorizable, final Authorizer authorizer, final RequestAction 
requestAction, final NiFiUser user) {
--- End diff --

There is no time when someone should invoke this method with a 
`requestAction` of `WRITE`. Can we remove that parameter?

Also, because of this and that the name is the same as the non-static 
version (which has admittedly confused me a number of times while reviewing) 
can we rename this method to more accurately depict its intention. Maybe 
something along the lines of `isOperationAuthorized`.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217390667
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-authorization/src/main/java/org/apache/nifi/authorization/resource/OperationAuthorizable.java
 ---
@@ -0,0 +1,85 @@
+/*
+ * 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.Authorizer;
+import org.apache.nifi.authorization.RequestAction;
+import org.apache.nifi.authorization.Resource;
+import org.apache.nifi.authorization.user.NiFiUser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Authorizable for a component that can be scheduled by operators.
+ */
+public class OperationAuthorizable implements Authorizable, 
EnforcePolicyPermissionsThroughBaseResource {
+private static Logger logger = 
LoggerFactory.getLogger(OperationAuthorizable.class);
+private final Authorizable baseAuthorizable;
+
+public OperationAuthorizable(final Authorizable baseAuthorizable) {
+this.baseAuthorizable = baseAuthorizable;
+}
+
+@Override
+public Authorizable getParentAuthorizable() {
+// Need to return parent operation authorizable. E.g. 
/operation/processor/ -> /operation/process-group/ -> 
/run-status/process-group/root
+if (baseAuthorizable.getParentAuthorizable() == null) {
+return null;
+} else {
+return new 
OperationAuthorizable(baseAuthorizable.getParentAuthorizable());
+}
+}
+
+@Override
+public Authorizable getBaseAuthorizable() {
+return baseAuthorizable;
+}
+
+@Override
+public Resource getResource() {
+return 
ResourceFactory.getOperationResource(baseAuthorizable.getResource());
+}
+
+/**
+ * Authorize the request action with the resource using base 
authorizable and operation authorizable combination.
+ *
+ * This method authorizes the request with the base authorizable 
first. If the request is allowed, then finish authorization.
+ * If base authorizable denies the request, then it checks if the user 
has WRITE permission for '/operation/{componentType}/{id}'.
+ */
+public static void authorize(final Authorizable baseAuthorizable, 
final Authorizer authorizer, final RequestAction requestAction, final NiFiUser 
user) {
--- End diff --

There is no time when someone should invoke this method with a 
`requestAction` of `WRITE`. Can we remove that parameter?

Also, because of this and that the name is the same as the non-static 
version (which has admittedly confused me a number of times while reviewing) 
can we rename this method to more accurately depict its intention. Maybe 
something along the lines of `authorizeOperation`.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217387530
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
--- End diff --

This should be calling `authorize` and not checking `isAuthorized`.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217415480
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-settings.js
 ---
@@ -1045,29 +1041,38 @@
 var reportingTaskActionFormatter = function (row, cell, value, 
columnDef, dataContext) {
 var markup = '';
 
-if (dataContext.permissions.canRead && 
dataContext.permissions.canWrite) {
-if (dataContext.component.state === 'RUNNING') {
+var canWrite = dataContext.permissions.canWrite;
+var canRead = dataContext.permissions.canRead;
+var canOperate = dataContext.operatePermissions.canWrite || 
canWrite;
+var isStopped = dataContext.status.runStatus === 'STOPPED';
+
+if (dataContext.status.runStatus === 'RUNNING') {
+if (canOperate) {
 markup += '';
-} else if (dataContext.component.state === 'STOPPED' || 
dataContext.component.state === 'DISABLED') {
-markup += '';
+}
 
-// support starting when stopped and no validation 
errors
-if (dataContext.component.state === 'STOPPED' && 
nfCommon.isEmpty(dataContext.component.validationErrors)) {
-markup += '';
-}
+} else if (isStopped || dataContext.status.runStatus === 
'DISABLED') {
 
-if (dataContext.component.multipleVersionsAvailable 
=== true) {
-markup += '';
-}
+if (canRead && canWrite) {
+markup += '';
+}
 
-if (nfCommon.canModifyController()) {
-markup += '';
-}
+// support starting when stopped and no validation errors
+if (canOperate && dataContext.status.runStatus === 
'STOPPED' && dataContext.status.validationStatus === 'VALID') {
+markup += '';
 }
 
-if (dataContext.component.persistsState === true) {
-markup += '';
+if (canRead && canWrite && 
dataContext.component.multipleVersionsAvailable === true) {
+markup += '';
 }
+
+if (nfCommon.canModifyController()) {
--- End diff --

I realize this isn't an issue with this PR but I believe this should be 
checking `canWrite` and not deferring to check the controller permission. The 
controller permission will have already been considered when determining a 
value for `canWrite` server-side since it should be the parent resource for 
every reporting task.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217401802
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
 ---
@@ -741,8 +740,8 @@
 })
 .attr('font-family', function (d) {
 var family = '';
-if (d.permissions.canRead) {
-if (hasIssues(d) || d.component.transmitting) {
+if (d.permissions.canRead || 
d.operatePermissions.canWrite) {
--- End diff --

These checks (here and below) also need to consider if the user has 
`d.permission.canWrite`. If the user only has operate permissions they can see 
the status. But if the user does not have operate but does have write to the 
RPG they currently cannot see the status icon.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217387691
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroupInputPort(id, 
createPortDTOWithDesiredRunStatus(portId, id, 
requestRemotePortRunStatusEntity)),
+(revision, remotePortRunStatusEntity) -> {
+// update the specified remote process group
+final RemoteProcessGroupPortEntity controllerResponse 
= serviceFacade.updateRemoteProcessGroupInputPort(revision, id,
+createPortDTOWithDesiredRunStatus(portId, id, 
remotePortRunStatusEntity));
+
+// get the 

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217405219
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-ui/src/main/webapp/js/nf/canvas/nf-remote-process-group.js
 ---
@@ -741,8 +740,8 @@
 })
 .attr('font-family', function (d) {
 var family = '';
-if (d.permissions.canRead) {
-if (hasIssues(d) || d.component.transmitting) {
+if (d.permissions.canRead || 
d.operatePermissions.canWrite) {
--- End diff --

Actually, for determining which icon we show and the appropriate style it 
appears that we are now only using the status. This is available to everyone so 
I don't think we need any permission checks here. We should only need them when 
adding the tooltips. This should be very similar to the Processor Invalid icon 
and the tooltips for the validation errors.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269769
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
 ---
@@ -542,6 +547,88 @@ public Response removeReportingTask(
 );
 }
 
+/**
+ * Updates the operational status for the specified ReportingTask with 
the specified values.
+ *
+ * @param httpServletRequest  request
+ * @param id  The id of the reporting task to update.
+ * @param requestRunStatus A runStatusEntity.
+ * @return A reportingTaskEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a reporting task",
+response = ReportingTaskEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/reporting-tasks/{uuid} or  or /operation/reporting-tasks/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The reporting task id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The reporting task run status.",
+required = true
+) final ReportingTaskRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Reporting task run status 
must be specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create DTO to verify if it can be updated.
+final ReportingTaskDTO reportingTaskDTO = new ReportingTaskDTO();
+reportingTaskDTO.setId(id);
+reportingTaskDTO.setState(requestRunStatus.getState());
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+// authorize reporting task
+final Authorizable authorizable = 
lookup.getReportingTask(id).getAuthorizable();
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateReportingTask(reportingTaskDTO),
+(revision, reportingTaskEntity) -> {
+// update the reporting task
+final ReportingTaskEntity entity = 
serviceFacade.updateReportingTask(revision, reportingTaskDTO);
--- End diff --

Fixed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269816
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/ComponentEntityMerger.java
 ---
@@ -68,10 +75,13 @@ default void merge(final EntityType clientEntity, final 
Map 
MAX_BULLETINS_PER_COMPONENT) {
 
clientEntity.setBulletins(clientEntity.getBulletins().subList(0, 
MAX_BULLETINS_PER_COMPONENT));
 }
+} else {
+clientEntity.setBulletins(null);
--- End diff --

Fixed. Thanks for caching this!


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269718
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+final RemoteProcessGroupPortDTO remoteProcessGroupPort = new 
RemoteProcessGroupPortDTO();
+remoteProcessGroupPort.setId(portId);
+remoteProcessGroupPort.setGroupId(id);
+
remoteProcessGroupPort.setTransmitting(shouldTransmit(requestRemotePortRunStatusEntity));
+
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroupInputPort(id, 
remoteProcessGroupPort),
+(revision, remoteProcessGroupPortEntity) -> {
+// update the specified remote process group
+final 

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269747
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -557,6 +750,90 @@ public Response updateRemoteProcessGroup(
 );
 }
 
+/**
+ * Updates the operational status for the specified remote process 
group with the specified value.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process group 
to update.
+ * @param requestRemotePortRunStatusEntity A remotePortRunStatusEntity.
+ * @return A remoteProcessGroupEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote process group",
+response = RemoteProcessGroupEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupRunStatus(
+@Context HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") String id,
+@ApiParam(
+value = "The remote process group run status.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group run 
status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+final RemoteProcessGroupDTO remoteProcessGroupDTO = new 
RemoteProcessGroupDTO();
+remoteProcessGroupDTO.setId(id);
+
remoteProcessGroupDTO.setTransmitting(shouldTransmit(requestRemotePortRunStatusEntity));
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+Authorizable authorizable = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroup(remoteProcessGroupDTO),
+(revision, remoteProcessGroupEntity) -> {
+// update the specified remote process group
+final RemoteProcessGroupEntity entity = 
serviceFacade.updateRemoteProcessGroup(revision, remoteProcessGroupDTO);
--- End diff --

Fixed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269686
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+final RemoteProcessGroupPortDTO remoteProcessGroupPort = new 
RemoteProcessGroupPortDTO();
+remoteProcessGroupPort.setId(portId);
+remoteProcessGroupPort.setGroupId(id);
+
remoteProcessGroupPort.setTransmitting(shouldTransmit(requestRemotePortRunStatusEntity));
+
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroupInputPort(id, 
remoteProcessGroupPort),
+(revision, remoteProcessGroupPortEntity) -> {
+// update the specified remote process group
+final 

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269622
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java
 ---
@@ -315,6 +319,90 @@ public Response removeOutputPort(
 );
 }
 
+
+/**
+ * Updates the operational status for the specified input port with 
the specified values.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the port to update.
+ * @param requestRunStatusA portRunStatusEntity.
+ * @return A portEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("/{id}/run-status")
+@ApiOperation(
+value = "Updates run status of an output-port",
+response = ProcessorEntity.class,
+authorizations = {
+@Authorization(value = "Write - /output-ports/{uuid} 
or /operation/output-ports/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The port id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The port run status.",
+required = true
+) final PortRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Port run status must be 
specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create port DTO to verify if it can be updated.
+final PortDTO portDTO = new PortDTO();
+portDTO.setId(id);
+portDTO.setState(requestRunStatus.getState());
+
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+final Authorizable authorizable = 
lookup.getOutputPort(id);
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, user);
+},
+() -> serviceFacade.verifyUpdateOutputPort(portDTO),
+(revision, runStatusEntity) -> {
+// update the input port
+final PortEntity entity = 
serviceFacade.updateOutputPort(revision, portDTO);
--- End diff --

Fixed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269601
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java
 ---
@@ -315,6 +319,90 @@ public Response removeInputPort(
 );
 }
 
+/**
+ * Updates the operational status for the specified input port with 
the specified values.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the port to update.
+ * @param requestRunStatusA portRunStatusEntity.
+ * @return A portEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("/{id}/run-status")
+@ApiOperation(
+value = "Updates run status of an input-port",
+response = ProcessorEntity.class,
+authorizations = {
+@Authorization(value = "Write - /input-ports/{uuid} or 
/operation/input-ports/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The port id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The port run status.",
+required = true
+) final PortRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Port run status must be 
specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create port DTO to verify if it can be updated.
+final PortDTO portDTO = new PortDTO();
+portDTO.setId(id);
+portDTO.setState(requestRunStatus.getState());
+
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+final Authorizable authorizable = 
lookup.getInputPort(id);
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, user);
+},
+() -> serviceFacade.verifyUpdateInputPort(portDTO),
+(revision, runStatusEntity) -> {
+// update the input port
+final PortEntity entity = 
serviceFacade.updateInputPort(revision, portDTO);
--- End diff --

Fixed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269661
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
 ---
@@ -668,6 +670,91 @@ public Response deleteProcessor(
 );
 }
 
+/**
+ * Updates the operational status for the specified processor with the 
specified values.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the processor to update.
+ * @param requestRunStatusA processorEntity.
+ * @return A processorEntity.
+ * @throws InterruptedException if interrupted
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("/{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a processor",
+response = ProcessorEntity.class,
+authorizations = {
+@Authorization(value = "Write - /processors/{uuid} or 
/operation/processors/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The processor id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The processor run status.",
+required = true
+) final ProcessorRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Processor run status must 
be specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create processor DTO to verify if it can be updated.
+final ProcessorDTO requestProcessorDTO = new ProcessorDTO();
+requestProcessorDTO.setId(id);
+requestProcessorDTO.setState(requestRunStatus.getState());
+
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+final Authorizable authorizable = 
lookup.getProcessor(id).getAuthorizable();
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, user);
+},
+() -> 
serviceFacade.verifyUpdateProcessor(requestProcessorDTO),
+(revision, runStatusEntity) -> {
+// update the processor
+final ProcessorEntity entity = 
serviceFacade.updateProcessor(revision, requestProcessorDTO);
--- End diff --

Fixed.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-13 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217269181
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
 ---
@@ -741,6 +743,88 @@ public Response removeControllerService(
 );
 }
 
+/**
+ * Updates the operational status for the specified controller service 
with the specified values.
+ *
+ * @param httpServletRequest  request
+ * @param id  The id of the controller service to 
update.
+ * @param requestRunStatusA runStatusEntity.
+ * @return A controllerServiceEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a controller service",
+response = ControllerServiceEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/controller-services/{uuid} or /operation/controller-services/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The controller service id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The controller service run status.",
+required = true
+) final ControllerServiceRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Controller service run 
status must be specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+}  else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create DTO to verify if it can be updated.
+final ControllerServiceDTO controllerServiceDTO = new 
ControllerServiceDTO();
+controllerServiceDTO.setId(id);
+controllerServiceDTO.setState(requestRunStatus.getState());
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+// authorize the service
+final Authorizable authorizable = 
lookup.getControllerService(id).getAuthorizable();
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateControllerService(controllerServiceDTO),
+(revision, runStatusEntity) -> {
+// update the controller service
+final ControllerServiceEntity entity = 
serviceFacade.updateControllerService(revision, controllerServiceDTO);
--- End diff --

To be honest, I don't fully understand how much this is going to be 
critical as the DTO and entity only have the desired state and guid, those are 
not changing during the two phase commit. I think I'm missing some scenario 
here. But I've modified the code to create DTO at each function that is passed 
to the `withWriteLock` method.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread ijokarumawak
Github user ijokarumawak commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217233852
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/ControllerServiceEntityMerger.java
 ---
@@ -137,7 +138,9 @@ public static void 
mergeControllerServiceReferences(final Set

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217134368
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ControllerServiceResource.java
 ---
@@ -741,6 +743,88 @@ public Response removeControllerService(
 );
 }
 
+/**
+ * Updates the operational status for the specified controller service 
with the specified values.
+ *
+ * @param httpServletRequest  request
+ * @param id  The id of the controller service to 
update.
+ * @param requestRunStatusA runStatusEntity.
+ * @return A controllerServiceEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a controller service",
+response = ControllerServiceEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/controller-services/{uuid} or /operation/controller-services/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The controller service id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The controller service run status.",
+required = true
+) final ControllerServiceRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Controller service run 
status must be specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+}  else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create DTO to verify if it can be updated.
+final ControllerServiceDTO controllerServiceDTO = new 
ControllerServiceDTO();
+controllerServiceDTO.setId(id);
+controllerServiceDTO.setState(requestRunStatus.getState());
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+// authorize the service
+final Authorizable authorizable = 
lookup.getControllerService(id).getAuthorizable();
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateControllerService(controllerServiceDTO),
+(revision, runStatusEntity) -> {
+// update the controller service
+final ControllerServiceEntity entity = 
serviceFacade.updateControllerService(revision, controllerServiceDTO);
--- End diff --

We need to recreate this `controllerServiceDTO` using the `runStatusEntity` 
due to how we authorize/cache requests during our two phase commit.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217134851
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/OutputPortResource.java
 ---
@@ -315,6 +319,90 @@ public Response removeOutputPort(
 );
 }
 
+
+/**
+ * Updates the operational status for the specified input port with 
the specified values.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the port to update.
+ * @param requestRunStatusA portRunStatusEntity.
+ * @return A portEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("/{id}/run-status")
+@ApiOperation(
+value = "Updates run status of an output-port",
+response = ProcessorEntity.class,
+authorizations = {
+@Authorization(value = "Write - /output-ports/{uuid} 
or /operation/output-ports/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The port id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The port run status.",
+required = true
+) final PortRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Port run status must be 
specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create port DTO to verify if it can be updated.
+final PortDTO portDTO = new PortDTO();
+portDTO.setId(id);
+portDTO.setState(requestRunStatus.getState());
+
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+final Authorizable authorizable = 
lookup.getOutputPort(id);
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, user);
+},
+() -> serviceFacade.verifyUpdateOutputPort(portDTO),
+(revision, runStatusEntity) -> {
+// update the input port
+final PortEntity entity = 
serviceFacade.updateOutputPort(revision, portDTO);
--- End diff --

We need to recreate this `portDTO` using the `runStatusEntity` due to how 
we authorize/cache requests during our two phase commit.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217135024
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ProcessorResource.java
 ---
@@ -668,6 +670,91 @@ public Response deleteProcessor(
 );
 }
 
+/**
+ * Updates the operational status for the specified processor with the 
specified values.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the processor to update.
+ * @param requestRunStatusA processorEntity.
+ * @return A processorEntity.
+ * @throws InterruptedException if interrupted
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("/{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a processor",
+response = ProcessorEntity.class,
+authorizations = {
+@Authorization(value = "Write - /processors/{uuid} or 
/operation/processors/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The processor id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The processor run status.",
+required = true
+) final ProcessorRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Processor run status must 
be specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create processor DTO to verify if it can be updated.
+final ProcessorDTO requestProcessorDTO = new ProcessorDTO();
+requestProcessorDTO.setId(id);
+requestProcessorDTO.setState(requestRunStatus.getState());
+
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+final Authorizable authorizable = 
lookup.getProcessor(id).getAuthorizable();
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, user);
+},
+() -> 
serviceFacade.verifyUpdateProcessor(requestProcessorDTO),
+(revision, runStatusEntity) -> {
+// update the processor
+final ProcessorEntity entity = 
serviceFacade.updateProcessor(revision, requestProcessorDTO);
--- End diff --

We need to recreate this `requestProcessorDTO` using the `runStatusEntity` 
due to how we authorize/cache requests during our two phase commit.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217140684
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/ComponentEntityMerger.java
 ---
@@ -68,10 +75,13 @@ default void merge(final EntityType clientEntity, final 
Map 
MAX_BULLETINS_PER_COMPONENT) {
 
clientEntity.setBulletins(clientEntity.getBulletins().subList(0, 
MAX_BULLETINS_PER_COMPONENT));
 }
+} else {
+clientEntity.setBulletins(null);
--- End diff --

I think we need to continue to set the component to null when `canRead` is 
false. It may have been changed to accommodate an earlier iteration of this PR.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217134678
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/InputPortResource.java
 ---
@@ -315,6 +319,90 @@ public Response removeInputPort(
 );
 }
 
+/**
+ * Updates the operational status for the specified input port with 
the specified values.
+ *
+ * @param httpServletRequest request
+ * @param id The id of the port to update.
+ * @param requestRunStatusA portRunStatusEntity.
+ * @return A portEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("/{id}/run-status")
+@ApiOperation(
+value = "Updates run status of an input-port",
+response = ProcessorEntity.class,
+authorizations = {
+@Authorization(value = "Write - /input-ports/{uuid} or 
/operation/input-ports/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The port id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The port run status.",
+required = true
+) final PortRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Port run status must be 
specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create port DTO to verify if it can be updated.
+final PortDTO portDTO = new PortDTO();
+portDTO.setId(id);
+portDTO.setState(requestRunStatus.getState());
+
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+final NiFiUser user = NiFiUserUtils.getNiFiUser();
+
+final Authorizable authorizable = 
lookup.getInputPort(id);
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, user);
+},
+() -> serviceFacade.verifyUpdateInputPort(portDTO),
+(revision, runStatusEntity) -> {
+// update the input port
+final PortEntity entity = 
serviceFacade.updateInputPort(revision, portDTO);
--- End diff --

We need to recreate this `portDTO` using the `runStatusEntity` due to how 
we authorize/cache requests during our two phase commit.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217135735
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -557,6 +750,90 @@ public Response updateRemoteProcessGroup(
 );
 }
 
+/**
+ * Updates the operational status for the specified remote process 
group with the specified value.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process group 
to update.
+ * @param requestRemotePortRunStatusEntity A remotePortRunStatusEntity.
+ * @return A remoteProcessGroupEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote process group",
+response = RemoteProcessGroupEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupRunStatus(
+@Context HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") String id,
+@ApiParam(
+value = "The remote process group run status.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group run 
status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+final RemoteProcessGroupDTO remoteProcessGroupDTO = new 
RemoteProcessGroupDTO();
+remoteProcessGroupDTO.setId(id);
+
remoteProcessGroupDTO.setTransmitting(shouldTransmit(requestRemotePortRunStatusEntity));
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+Authorizable authorizable = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroup(remoteProcessGroupDTO),
+(revision, remoteProcessGroupEntity) -> {
+// update the specified remote process group
+final RemoteProcessGroupEntity entity = 
serviceFacade.updateRemoteProcessGroup(revision, remoteProcessGroupDTO);
--- End diff --

We need to recreate this `remoteProcessGroupDTO` using the 
`remoteProcessGroupEntity` due to how we authorize/cache requests during our 
two phase commit.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217135560
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+final RemoteProcessGroupPortDTO remoteProcessGroupPort = new 
RemoteProcessGroupPortDTO();
+remoteProcessGroupPort.setId(portId);
+remoteProcessGroupPort.setGroupId(id);
+
remoteProcessGroupPort.setTransmitting(shouldTransmit(requestRemotePortRunStatusEntity));
+
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroupInputPort(id, 
remoteProcessGroupPort),
+(revision, remoteProcessGroupPortEntity) -> {
+// update the specified remote process group
+final 

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217135375
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/RemoteProcessGroupResource.java
 ---
@@ -434,6 +436,197 @@ public Response updateRemoteProcessGroupOutputPort(
 );
 }
 
+/**
+ * Updates the specified remote process group input port run status.
+ *
+ * @param httpServletRequest   request
+ * @param id   The id of the remote process 
group to update.
+ * @param portId   The id of the input port to 
update.
+ * @param requestRemotePortRunStatusEntity The 
remoteProcessGroupPortRunStatusEntity
+ * @return A remoteProcessGroupPortEntity
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/input-ports/{port-id}/run-status")
+@ApiOperation(
+value = "Updates run status of a remote port",
+notes = NON_GUARANTEED_ENDPOINT,
+response = RemoteProcessGroupPortEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/remote-process-groups/{uuid} or /operation/remote-process-groups/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRemoteProcessGroupInputPortRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The remote process group id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The remote process group port id.",
+required = true
+)
+@PathParam("port-id") final String portId,
+@ApiParam(
+value = "The remote process group port.",
+required = true
+) final RemotePortRunStatusEntity 
requestRemotePortRunStatusEntity) {
+
+if (requestRemotePortRunStatusEntity == null) {
+throw new IllegalArgumentException("Remote process group port 
run status must be specified.");
+}
+
+if (requestRemotePortRunStatusEntity.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRemotePortRunStatusEntity.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, 
requestRemotePortRunStatusEntity);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRemotePortRunStatusEntity.isDisconnectedNodeAcknowledged());
+}
+
+final Revision requestRevision = 
getRevision(requestRemotePortRunStatusEntity.getRevision(), id);
+final RemoteProcessGroupPortDTO remoteProcessGroupPort = new 
RemoteProcessGroupPortDTO();
+remoteProcessGroupPort.setId(portId);
+remoteProcessGroupPort.setGroupId(id);
+
remoteProcessGroupPort.setTransmitting(shouldTransmit(requestRemotePortRunStatusEntity));
+
+return withWriteLock(
+serviceFacade,
+requestRemotePortRunStatusEntity,
+requestRevision,
+lookup -> {
+final Authorizable remoteProcessGroup = 
lookup.getRemoteProcessGroup(id);
+OperationAuthorizable.isAuthorized(remoteProcessGroup, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateRemoteProcessGroupInputPort(id, 
remoteProcessGroupPort),
+(revision, remoteProcessGroupPortEntity) -> {
+// update the specified remote process group
+final 

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217136120
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/api/ReportingTaskResource.java
 ---
@@ -542,6 +547,88 @@ public Response removeReportingTask(
 );
 }
 
+/**
+ * Updates the operational status for the specified ReportingTask with 
the specified values.
+ *
+ * @param httpServletRequest  request
+ * @param id  The id of the reporting task to update.
+ * @param requestRunStatus A runStatusEntity.
+ * @return A reportingTaskEntity.
+ */
+@PUT
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+@Path("{id}/run-status")
+@ApiOperation(
+value = "Updates run status of a reporting task",
+response = ReportingTaskEntity.class,
+authorizations = {
+@Authorization(value = "Write - 
/reporting-tasks/{uuid} or  or /operation/reporting-tasks/{uuid}")
+}
+)
+@ApiResponses(
+value = {
+@ApiResponse(code = 400, message = "NiFi was unable to 
complete the request because it was invalid. The request should not be retried 
without modification."),
+@ApiResponse(code = 401, message = "Client could not 
be authenticated."),
+@ApiResponse(code = 403, message = "Client is not 
authorized to make this request."),
+@ApiResponse(code = 404, message = "The specified 
resource could not be found."),
+@ApiResponse(code = 409, message = "The request was 
valid but NiFi was not in the appropriate state to process it. Retrying the 
same request later may be successful.")
+}
+)
+public Response updateRunStatus(
+@Context final HttpServletRequest httpServletRequest,
+@ApiParam(
+value = "The reporting task id.",
+required = true
+)
+@PathParam("id") final String id,
+@ApiParam(
+value = "The reporting task run status.",
+required = true
+) final ReportingTaskRunStatusEntity requestRunStatus) {
+
+if (requestRunStatus == null) {
+throw new IllegalArgumentException("Reporting task run status 
must be specified.");
+}
+
+if (requestRunStatus.getRevision() == null) {
+throw new IllegalArgumentException("Revision must be 
specified.");
+}
+
+requestRunStatus.validateState();
+
+if (isReplicateRequest()) {
+return replicate(HttpMethod.PUT, requestRunStatus);
+} else if (isDisconnectedFromCluster()) {
+
verifyDisconnectedNodeModification(requestRunStatus.isDisconnectedNodeAcknowledged());
+}
+
+// handle expects request (usually from the cluster manager)
+final Revision requestRevision = 
getRevision(requestRunStatus.getRevision(), id);
+// Create DTO to verify if it can be updated.
+final ReportingTaskDTO reportingTaskDTO = new ReportingTaskDTO();
+reportingTaskDTO.setId(id);
+reportingTaskDTO.setState(requestRunStatus.getState());
+return withWriteLock(
+serviceFacade,
+requestRunStatus,
+requestRevision,
+lookup -> {
+// authorize reporting task
+final Authorizable authorizable = 
lookup.getReportingTask(id).getAuthorizable();
+OperationAuthorizable.authorize(authorizable, 
authorizer, RequestAction.WRITE, NiFiUserUtils.getNiFiUser());
+},
+() -> 
serviceFacade.verifyUpdateReportingTask(reportingTaskDTO),
+(revision, reportingTaskEntity) -> {
+// update the reporting task
+final ReportingTaskEntity entity = 
serviceFacade.updateReportingTask(revision, reportingTaskDTO);
--- End diff --

We need to recreate this `reportingTaskDTO` using the `reportingTaskEntity` 
due to how we authorize/cache requests during our two phase commit.


---


[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-12 Thread mcgilman
Github user mcgilman commented on a diff in the pull request:

https://github.com/apache/nifi/pull/2990#discussion_r217121570
  
--- Diff: 
nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster/src/main/java/org/apache/nifi/cluster/manager/ControllerServiceEntityMerger.java
 ---
@@ -137,7 +138,9 @@ public static void 
mergeControllerServiceReferences(final Set

[GitHub] nifi pull request #2990: NIFI-375: Added operation policy

2018-09-05 Thread ijokarumawak
GitHub user ijokarumawak opened a pull request:

https://github.com/apache/nifi/pull/2990

NIFI-375: Added operation policy

The operation policy allows that a user to operate components even if they 
does not have direct READ/WRITE permission of the component.

Following operations are controlled by the new operate policy:
- Start/stop/enable/disable Processors, ControllerServices,
ReportingTasks, Input/OuputPorts
- Enable/disable transmission of RemoteInput/OutputPorts and
RemoteProcessGroups
- Terminate Processor threads

Thank you for submitting a contribution to Apache NiFi.

In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:

### For all changes:
- [x] Is there a JIRA ticket associated with this PR? Is it referenced 
 in the commit message?

- [x] Does your PR title start with NIFI- where  is the JIRA number 
you are trying to resolve? Pay particular attention to the hyphen "-" character.

- [x] Has your PR been rebased against the latest commit within the target 
branch (typically master)?

- [x] Is your initial contribution a single, squashed commit?

### 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?
- [x] Have you written or updated unit tests to verify your changes?
- [ ] 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:
- [x] 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 travis-ci for build 
issues and submit an update to your PR as soon as possible.


You can merge this pull request into a Git repository by running:

$ git pull https://github.com/ijokarumawak/nifi nifi-375

Alternatively you can review and apply these changes as the patch at:

https://github.com/apache/nifi/pull/2990.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

This closes #2990


commit 96ec577e63461664511c3ac8da96f976a9166a7d
Author: Koji Kawamura 
Date:   2018-08-24T08:58:25Z

NIFI-375: Added operation policy

The operation policy allows that a user to operate components even if they 
does not have direct READ/WRITE
permission of the component.

Following operations are controlled by the new operate policy:
- Start/stop/enable/disable Processors, ControllerServices,
ReportingTasks, Input/OuputPorts
- Enable/disable transmission of RemoteInput/OutputPorts and
RemoteProcessGroups
- Terminate Processor threads




---