diff --git a/nifi-api/src/main/java/org/apache/nifi/action/Component.java b/nifi-api/src/main/java/org/apache/nifi/action/Component.java index e9997d5902..77c84a881e 100644 --- a/nifi-api/src/main/java/org/apache/nifi/action/Component.java +++ b/nifi-api/src/main/java/org/apache/nifi/action/Component.java @@ -36,5 +36,6 @@ public enum Component { ParameterProvider, AccessPolicy, User, - UserGroup; + UserGroup, + Label; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/LabelAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/LabelAuditor.java new file mode 100644 index 0000000000..87986f4d37 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/LabelAuditor.java @@ -0,0 +1,177 @@ +/* + * 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.audit; + +import org.apache.nifi.action.Action; +import org.apache.nifi.action.Component; +import org.apache.nifi.action.FlowChangeAction; +import org.apache.nifi.action.Operation; +import org.apache.nifi.action.details.ActionDetails; +import org.apache.nifi.action.details.FlowChangeConfigureDetails; +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.controller.label.Label; +import org.apache.nifi.web.api.dto.LabelDTO; +import org.apache.nifi.web.dao.LabelDAO; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Date; +import java.util.Objects; + +@Aspect +public class LabelAuditor extends NiFiAuditor { + private static final Logger logger = LoggerFactory.getLogger(LabelAuditor.class); + + /** + * Audits the creation of a Label. + * + * @param proceedingJoinPoint Join Point observed + * @return Label + * @throws Throwable Thrown on failure to proceed with target invocation + */ + @Around("within(org.apache.nifi.web.dao.LabelDAO+) && " + + "execution(org.apache.nifi.controller.label.Label createLabel(java.lang.String, org.apache.nifi.web.api.dto.LabelDTO))") + public Label createLabelAdvice(final ProceedingJoinPoint proceedingJoinPoint) throws Throwable { + final Label label = (Label) proceedingJoinPoint.proceed(); + final Action action = generateAuditRecord(label, Operation.Add); + + if (action != null) { + saveAction(action, logger); + } + + return label; + } + + /** + * Audits the configuration of a Label. + * + * @param proceedingJoinPoint Join Point observed + * @param labelDTO Data Transfer Object + * @param labelDAO Data Access Object + * @return Label + * @throws Throwable Thrown on failure to proceed with target invocation + */ + @Around("within(org.apache.nifi.web.dao.LabelDAO+) && " + + "execution(org.apache.nifi.controller.label.Label updateLabel(org.apache.nifi.web.api.dto.LabelDTO)) && " + + "args(labelDTO) && " + + "target(labelDAO)") + public Label updateLabelAdvice(final ProceedingJoinPoint proceedingJoinPoint, final LabelDTO labelDTO, final LabelDAO labelDAO) throws Throwable { + // determine the initial content of label + final Label label = labelDAO.getLabel(labelDTO.getId()); + final String originalLabelValue = label.getValue(); + + final Label updatedLabel = (Label) proceedingJoinPoint.proceed(); + + // ensure the user was found + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user != null) { + final String updatedLabelValue = updatedLabel.getValue(); + if ((originalLabelValue == null && updatedLabelValue != null) + || !Objects.equals(originalLabelValue, updatedLabelValue)) { + final FlowChangeAction labelAction = new FlowChangeAction(); + labelAction.setUserIdentity(user.getIdentity()); + labelAction.setTimestamp(new Date()); + labelAction.setSourceId(label.getIdentifier()); + labelAction.setSourceType(Component.Label); + labelAction.setOperation(Operation.Configure); + // Source Name is a required field for the database but not applicable for a label; use UUID to create a unique name + labelAction.setSourceName(label.getIdentifier()); + + final FlowChangeConfigureDetails actionDetails = new FlowChangeConfigureDetails(); + actionDetails.setName(label.getIdentifier()); + actionDetails.setValue(updatedLabelValue); + actionDetails.setPreviousValue(originalLabelValue); + labelAction.setActionDetails(actionDetails); + + saveAction(labelAction, logger); + } + } + return updatedLabel; + } + + /** + * Audits the removal of a Label. + * + * @param proceedingJoinPoint Join Point observed + * @param labelId Label identifier + * @param labelDAO Label Data Access Object + * @throws Throwable Thrown on failure to proceed with target invocation + */ + @Around("within(org.apache.nifi.web.dao.LabelDAO+) && " + + "execution(void deleteLabel(java.lang.String)) && " + + "args(labelId) && " + + "target(labelDAO)") + public void removeLabelAdvice(final ProceedingJoinPoint proceedingJoinPoint, final String labelId, final LabelDAO labelDAO) throws Throwable { + // get the label before removing it + final Label label = labelDAO.getLabel(labelId); + + // remove the label + proceedingJoinPoint.proceed(); + + // if no exceptions were thrown, add removal actions... + final Action action = generateAuditRecord(label, Operation.Remove); + + if (action != null) { + saveAction(action, logger); + } + } + + /** + * Generates an audit record for the creation of the specified label. + * + * @param label Label audited + * @param operation Operation audited + * @return Action description + */ + public Action generateAuditRecord(final Label label, final Operation operation) { + return generateAuditRecord(label, operation, null); + } + + /** + * Generates an audit record for the creation of the specified label. + * + * @param label Label audited + * @param operation Operation audited + * @param actionDetails Action Details or null when not provided + * @return Action description + */ + public Action generateAuditRecord(final Label label, final Operation operation, final ActionDetails actionDetails) { + FlowChangeAction action = null; + + final NiFiUser user = NiFiUserUtils.getNiFiUser(); + if (user != null) { + action = new FlowChangeAction(); + action.setUserIdentity(user.getIdentity()); + action.setOperation(operation); + action.setTimestamp(new Date()); + action.setSourceId(label.getIdentifier()); + // Labels do not have a Name; use UUID to provide a unique name + action.setSourceName(label.getIdentifier()); + action.setSourceType(Component.Label); + + if (actionDetails != null) { + action.setActionDetails(actionDetails); + } + } + + return action; + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java index af3d19ba8e..8b47037daf 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/SnippetAuditor.java @@ -33,6 +33,7 @@ import org.apache.nifi.connectable.Funnel; import org.apache.nifi.connectable.Port; import org.apache.nifi.controller.ProcessorNode; import org.apache.nifi.controller.Snippet; +import org.apache.nifi.controller.label.Label; import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.groups.RemoteProcessGroup; import org.apache.nifi.authorization.user.NiFiUser; @@ -47,6 +48,7 @@ import org.apache.nifi.web.api.dto.RemoteProcessGroupDTO; import org.apache.nifi.web.api.dto.SnippetDTO; import org.apache.nifi.web.dao.ConnectionDAO; import org.apache.nifi.web.dao.FunnelDAO; +import org.apache.nifi.web.dao.LabelDAO; import org.apache.nifi.web.dao.PortDAO; import org.apache.nifi.web.dao.ProcessGroupDAO; import org.apache.nifi.web.dao.ProcessorDAO; @@ -78,6 +80,7 @@ public class SnippetAuditor extends NiFiAuditor { private ProcessorDAO processorDAO; private FunnelDAO funnelDAO; private ConnectionDAO connectionDAO; + private LabelDAO labelDAO; private PortAuditor portAuditor; private RemoteProcessGroupAuditor remoteProcessGroupAuditor; @@ -85,6 +88,7 @@ public class SnippetAuditor extends NiFiAuditor { private ProcessorAuditor processorAuditor; private FunnelAuditor funnelAuditor; private RelationshipAuditor relationshipAuditor; + private LabelAuditor labelAuditor; /** * Audits copy/paste. @@ -328,6 +332,14 @@ public class SnippetAuditor extends NiFiAuditor { } } + for (String id : snippet.getLabels().keySet()) { + final Label label = labelDAO.getLabel(id); + final Action action = labelAuditor.generateAuditRecord(label, Operation.Move, createMoveDetails(previousGroupId, groupId, logger)); + if (action != null) { + actions.add(action); + } + } + // save the actions if (CollectionUtils.isNotEmpty(actions)) { saveActions(actions, logger); @@ -390,6 +402,11 @@ public class SnippetAuditor extends NiFiAuditor { connections.add(connectionDAO.getConnection(id)); } + final Set