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 a25525fa79..95d8ba066d 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 @@ -31,6 +31,7 @@ public enum Component { Connection, ControllerService, ReportingTask, + ParameterContext, AccessPolicy, User, UserGroup; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ParameterContextAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ParameterContextAuditor.java new file mode 100644 index 0000000000..223ada6594 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ParameterContextAuditor.java @@ -0,0 +1,284 @@ +/* + * 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.FlowChangeConfigureDetails; +import org.apache.nifi.authorization.user.NiFiUser; +import org.apache.nifi.authorization.user.NiFiUserUtils; +import org.apache.nifi.parameter.Parameter; +import org.apache.nifi.parameter.ParameterContext; +import org.apache.nifi.web.api.dto.ParameterContextDTO; +import org.apache.nifi.web.api.dto.ParameterDTO; +import org.apache.nifi.web.dao.ParameterContextDAO; +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.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +/** + * Audits processor creation/removal and configuration changes. + */ +@Aspect +public class ParameterContextAuditor extends NiFiAuditor { + + private static final Logger logger = LoggerFactory.getLogger(ParameterContextAuditor.class); + + /** + * Audits the creation of parameter contexts via createParameterContext(). + * + * @param proceedingJoinPoint join point + * @param parameterContextDTO dto + * @param parameterContextDAO dao + * @return context + * @throws Throwable ex + */ + @Around("within(org.apache.nifi.web.dao.ParameterContextDAO+) && " + + "execution(org.apache.nifi.parameter.ParameterContext createParameterContext(org.apache.nifi.web.api.dto.ParameterContextDTO)) && " + + "args(parameterContextDTO) && " + + "target(parameterContextDAO)") + public ParameterContext createParameterContextAdvice(ProceedingJoinPoint proceedingJoinPoint, ParameterContextDTO parameterContextDTO, ParameterContextDAO parameterContextDAO) throws Throwable { + // update the processor state + ParameterContext parameterContext = (ParameterContext) proceedingJoinPoint.proceed(); + + // get the current user + NiFiUser user = NiFiUserUtils.getNiFiUser(); + + // ensure the user was found + if (user != null) { + // create a parameter context action + Collection actions = new ArrayList<>(); + + // if no exceptions were thrown, add the processor action... + final Action createAction = generateAuditRecord(parameterContext, Operation.Add); + actions.add(createAction); + + // determine the updated values + Map updatedValues = extractConfiguredParameterContextValues(parameterContext, parameterContextDTO); + + // determine the actions performed in this request + final Date actionTimestamp = new Date(createAction.getTimestamp().getTime() + 1); + determineActions(user, parameterContext, actions, actionTimestamp, updatedValues, Collections.EMPTY_MAP); + + // save the actions + saveActions(actions, logger); + } + + return parameterContext; + } + + /** + * Audits the configuration of a parameter context via updateParameterContext(). + * + * @param proceedingJoinPoint join point + * @param parameterContextDTO dto + * @param parameterContextDAO dao + * @return parameter context + * @throws Throwable ex + */ + @Around("within(org.apache.nifi.web.dao.ParameterContextDAO+) && " + + "execution(org.apache.nifi.parameter.ParameterContext updateParameterContext(org.apache.nifi.web.api.dto.ParameterContextDTO)) && " + + "args(parameterContextDTO) && " + + "target(parameterContextDAO)") + public ParameterContext updateParameterContextAdvice(ProceedingJoinPoint proceedingJoinPoint, ParameterContextDTO parameterContextDTO, ParameterContextDAO parameterContextDAO) throws Throwable { + // determine the initial values for each property/setting that's changing + ParameterContext parameterContext = parameterContextDAO.getParameterContext(parameterContextDTO.getId()); + final Map values = extractConfiguredParameterContextValues(parameterContext, parameterContextDTO); + + // update the processor state + final ParameterContext updatedParameterContext = (ParameterContext) proceedingJoinPoint.proceed(); + + // if no exceptions were thrown, add the processor action... + parameterContext = parameterContextDAO.getParameterContext(updatedParameterContext.getIdentifier()); + + // get the current user + NiFiUser user = NiFiUserUtils.getNiFiUser(); + + // ensure the user was found + if (user != null) { + // determine the updated values + Map updatedValues = extractConfiguredParameterContextValues(parameterContext, parameterContextDTO); + + // create a parameter context action + Date actionTimestamp = new Date(); + Collection actions = new ArrayList<>(); + + // determine the actions performed in this request + determineActions(user, parameterContext, actions, actionTimestamp, updatedValues, values); + + // ensure there are actions to record + if (!actions.isEmpty()) { + // save the actions + saveActions(actions, logger); + } + } + + return updatedParameterContext; + } + + /** + * Extract configuration changes. + * + * @param user the user + * @param parameterContext the parameter context + * @param actions actions list + * @param actionTimestamp timestamp of the request + * @param updatedValues the updated values + * @param values the current values + */ + private void determineActions(final NiFiUser user, final ParameterContext parameterContext, final Collection actions, + final Date actionTimestamp, final Map updatedValues, final Map values) { + + // go through each updated value + for (String key : updatedValues.keySet()) { + String newValue = updatedValues.get(key); + String oldValue = values.get(key); + Operation operation = null; + + // determine the type of operation + if (oldValue == null || newValue == null || !newValue.equals(oldValue)) { + operation = Operation.Configure; + } + + // create a configuration action accordingly + if (operation != null) { + // clear the value if this property is sensitive + final Parameter parameter = parameterContext.getParameter(key).orElse(null); + if (parameter != null && parameter.getDescriptor().isSensitive()) { + if (newValue != null) { + newValue = "********"; + } + if (oldValue != null) { + oldValue = "********"; + } + } + + final FlowChangeConfigureDetails actionDetails = new FlowChangeConfigureDetails(); + actionDetails.setName(key); + actionDetails.setValue(newValue); + actionDetails.setPreviousValue(oldValue); + + // create a configuration action + FlowChangeAction configurationAction = new FlowChangeAction(); + configurationAction.setUserIdentity(user.getIdentity()); + configurationAction.setOperation(operation); + configurationAction.setTimestamp(actionTimestamp); + configurationAction.setSourceId(parameterContext.getIdentifier()); + configurationAction.setSourceName(parameterContext.getName()); + configurationAction.setSourceType(Component.ParameterContext); + configurationAction.setActionDetails(actionDetails); + actions.add(configurationAction); + } + } + } + + /** + * Audits the removal of a parameter context via deleteParameterContext(). + * + * @param proceedingJoinPoint join point + * @param parameterContextId parameterContextId + * @param parameterContextDAO dao + * @throws Throwable ex + */ + @Around("within(org.apache.nifi.web.dao.ParameterContextDAO+) && " + + "execution(void deleteParameterContext(java.lang.String)) && " + + "args(parameterContextId) && " + + "target(parameterContextDAO)") + public void removeParameterContextAdvice(ProceedingJoinPoint proceedingJoinPoint, String parameterContextId, ParameterContextDAO parameterContextDAO) throws Throwable { + // get the parameter context before removing it + ParameterContext parameterContext = parameterContextDAO.getParameterContext(parameterContextId); + + // remove the processor + proceedingJoinPoint.proceed(); + + // if no exceptions were thrown, add removal actions... + // audit the processor removal + final Action action = generateAuditRecord(parameterContext, Operation.Remove); + + // save the actions + if (action != null) { + saveAction(action, logger); + } + } + + /** + * Generates an audit record for the creation of a ParameterContext. + * + * @param parameterContext parameterContext + * @param operation operation + * @return action + */ + private Action generateAuditRecord(ParameterContext parameterContext, Operation operation) { + FlowChangeAction action = null; + + // get the current user + NiFiUser user = NiFiUserUtils.getNiFiUser(); + + // ensure the user was found + if (user != null) { + // create the processor action for adding this processor + action = new FlowChangeAction(); + action.setUserIdentity(user.getIdentity()); + action.setOperation(operation); + action.setTimestamp(new Date()); + action.setSourceId(parameterContext.getIdentifier()); + action.setSourceName(parameterContext.getName()); + action.setSourceType(Component.ParameterContext); + } + + return action; + } + + /** + * Extracts the values for the configured fields from the specified ParameterContext. + */ + private Map extractConfiguredParameterContextValues(ParameterContext parameterContext, ParameterContextDTO parameterContextDTO) { + Map values = new HashMap<>(); + + if (parameterContextDTO.getDescription() != null) { + values.put("Name", parameterContext.getName()); + } + if (parameterContextDTO.getDescription() != null) { + values.put("Description", parameterContext.getDescription()); + } + if (parameterContextDTO.getParameters() != null) { + parameterContextDTO.getParameters().forEach(parameterEntity -> { + final ParameterDTO parameterDTO = parameterEntity.getParameter(); + final Parameter parameter = parameterContext.getParameter(parameterDTO.getName()).orElse(null); + if (parameter == null) { + values.put(parameterDTO.getName(), null); + } else { + values.put(parameterDTO.getName(), parameter.getValue()); + } + }); + } + + return values; + } + +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java index 3d92f5bc95..c1b43aa048 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/audit/ProcessGroupAuditor.java @@ -28,6 +28,7 @@ import org.apache.nifi.authorization.user.NiFiUserUtils; import org.apache.nifi.controller.ScheduledState; import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.groups.ProcessGroup; +import org.apache.nifi.parameter.ParameterContext; import org.apache.nifi.registry.flow.VersionControlInformation; import org.apache.nifi.web.api.dto.ProcessGroupDTO; import org.apache.nifi.web.api.dto.VariableRegistryDTO; @@ -97,6 +98,7 @@ public class ProcessGroupAuditor extends NiFiAuditor { ProcessGroup processGroup = processGroupDAO.getProcessGroup(processGroupDTO.getId()); String name = processGroup.getName(); + ParameterContext parameterContext = processGroup.getParameterContext(); String comments = processGroup.getComments(); // perform the underlying operation @@ -113,7 +115,7 @@ public class ProcessGroupAuditor extends NiFiAuditor { if (name != null && updatedProcessGroup.getName() != null && !name.equals(updatedProcessGroup.getName())) { // create the config details FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails(); - configDetails.setName("name"); + configDetails.setName("Name"); configDetails.setValue(updatedProcessGroup.getName()); configDetails.setPreviousValue(name); @@ -124,13 +126,42 @@ public class ProcessGroupAuditor extends NiFiAuditor { if (comments != null && updatedProcessGroup.getComments() != null && !comments.equals(updatedProcessGroup.getComments())) { // create the config details FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails(); - configDetails.setName("comments"); + configDetails.setName("Comments"); configDetails.setValue(updatedProcessGroup.getComments()); configDetails.setPreviousValue(comments); details.add(configDetails); } + // see if the parameter context has changed + if (parameterContext != null && updatedProcessGroup.getParameterContext() != null) { + if (!parameterContext.getIdentifier().equals(updatedProcessGroup.getParameterContext().getIdentifier())) { + // create the config details + FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails(); + configDetails.setName("Parameter Context"); + configDetails.setValue(updatedProcessGroup.getParameterContext().getIdentifier()); + configDetails.setPreviousValue(parameterContext.getIdentifier()); + + details.add(configDetails); + } + } else if (updatedProcessGroup.getParameterContext() != null) { + // create the config details + FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails(); + configDetails.setName("Parameter Context"); + configDetails.setValue(updatedProcessGroup.getParameterContext().getIdentifier()); + configDetails.setPreviousValue(null); + + details.add(configDetails); + } else if (parameterContext != null) { + // create the config details + FlowChangeConfigureDetails configDetails = new FlowChangeConfigureDetails(); + configDetails.setName("Parameter Context"); + configDetails.setValue(null); + configDetails.setPreviousValue(parameterContext.getIdentifier()); + + details.add(configDetails); + } + // hold all actions Collection actions = new ArrayList<>(); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java index 8ebeec97d3..1dba3f1ffa 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/StandardNiFiServiceFacade.java @@ -5016,6 +5016,9 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { case Connection: authorizable = authorizableLookup.getConnection(sourceId).getAuthorizable(); break; + case ParameterContext: + authorizable = authorizableLookup.getParameterContext(sourceId); + break; case AccessPolicy: authorizable = authorizableLookup.getAccessPolicyById(sourceId); break; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml index c2b2b6828d..72ffc2ca33 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/resources/nifi-web-api-context.xml @@ -546,6 +546,11 @@ + + + + +