diff --git a/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java b/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java index 95a8c09649..acaffd7c03 100644 --- a/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java +++ b/nifi-api/src/main/java/org/apache/nifi/components/ValidationContext.java @@ -106,4 +106,12 @@ public interface ValidationContext extends PropertyContext { * @return true if a Parameter with the given name is defined in the currently selected Parameter Context */ boolean isParameterDefined(String parameterName); + + /** + * Returns true if a Parameter with the given name is defined and has a non-null value, false if either the Parameter + * is not defined or the Parameter is defined but has a value of null. + * @param parameterName the name of the parameter + * @return true if the Parameter is defined and has a non-null value, false otherwise + */ + boolean isParameterSet(String parameterName); } diff --git a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java index 8f7eda0e31..0075df20e4 100644 --- a/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java +++ b/nifi-bootstrap/src/main/java/org/apache/nifi/bootstrap/notification/NotificationValidationContext.java @@ -133,4 +133,9 @@ public class NotificationValidationContext implements ValidationContext { public boolean isParameterDefined(final String parameterName) { return false; } + + @Override + public boolean isParameterSet(final String parameterName) { + return false; + } } diff --git a/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java b/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java index 87389be797..e913204c57 100644 --- a/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java +++ b/nifi-mock/src/main/java/org/apache/nifi/util/MockValidationContext.java @@ -181,8 +181,12 @@ public class MockValidationContext extends MockControllerServiceLookup implement @Override public boolean isParameterDefined(final String parameterName) { - // TODO: Implement - return false; + return true; + } + + @Override + public boolean isParameterSet(final String parameterName) { + return true; } } diff --git a/nifi-nar-bundles/nifi-extension-utils/nifi-database-utils/src/test/java/org/apache/nifi/util/db/TestJdbcCommon.java b/nifi-nar-bundles/nifi-extension-utils/nifi-database-utils/src/test/java/org/apache/nifi/util/db/TestJdbcCommon.java index b58289ba13..bbf2bdb4a9 100644 --- a/nifi-nar-bundles/nifi-extension-utils/nifi-database-utils/src/test/java/org/apache/nifi/util/db/TestJdbcCommon.java +++ b/nifi-nar-bundles/nifi-extension-utils/nifi-database-utils/src/test/java/org/apache/nifi/util/db/TestJdbcCommon.java @@ -16,13 +16,26 @@ */ package org.apache.nifi.util.db; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; +import org.apache.avro.Conversions; +import org.apache.avro.LogicalType; +import org.apache.avro.LogicalTypes; +import org.apache.avro.Schema; +import org.apache.avro.file.DataFileStream; +import org.apache.avro.generic.GenericData; +import org.apache.avro.generic.GenericDatumReader; +import org.apache.avro.generic.GenericRecord; +import org.apache.avro.io.DatumReader; +import org.apache.avro.util.Utf8; +import org.apache.commons.io.input.ReaderInputStream; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.mockito.Mockito; +import org.mockito.stubbing.Answer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -63,26 +76,13 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.stream.IntStream; -import org.apache.avro.Conversions; -import org.apache.avro.LogicalType; -import org.apache.avro.LogicalTypes; -import org.apache.avro.Schema; -import org.apache.avro.file.DataFileStream; -import org.apache.avro.generic.GenericData; -import org.apache.avro.generic.GenericDatumReader; -import org.apache.avro.generic.GenericRecord; -import org.apache.avro.io.DatumReader; -import org.apache.avro.util.Utf8; -import org.apache.commons.io.input.ReaderInputStream; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.ClassRule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.mockito.Mockito; -import org.mockito.stubbing.Answer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class TestJdbcCommon { diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java index f5752a35cf..48e359f34e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/AbstractComponentNode.java @@ -659,6 +659,14 @@ public abstract class AbstractComponentNode implements ComponentNode { .explanation("Property references Parameter '" + paramName + "' but the currently selected Parameter Context does not have a Parameter with that name") .build()); } + + if (!validationContext.isParameterSet(paramName)) { + results.add(new ValidationResult.Builder() + .subject(propertyDescriptor.getDisplayName()) + .valid(false) + .explanation("Property references Parameter '" + paramName + "' but the currently selected Parameter Context does not have a value set for that Parameter") + .build()); + } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java index 4d108e9c8a..d0726981bc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/controller/flow/FlowManager.java @@ -36,6 +36,7 @@ import org.apache.nifi.web.api.dto.FlowSnippetDTO; import java.net.URL; import java.util.Collection; +import java.util.Map; import java.util.Optional; import java.util.Set; @@ -322,7 +323,7 @@ public interface FlowManager { void removeRootControllerService(final ControllerServiceNode service); - ParameterContext createParameterContext(String id, String name, Set parameters); + ParameterContext createParameterContext(String id, String name, Map parameters); ParameterContextManager getParameterContextManager(); } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java index dae4d9e22d..fcbd270e8e 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core-api/src/main/java/org/apache/nifi/parameter/ParameterContext.java @@ -20,7 +20,6 @@ import org.apache.nifi.authorization.resource.Authorizable; import java.util.Map; import java.util.Optional; -import java.util.Set; public interface ParameterContext extends ParameterLookup, Authorizable { @@ -52,19 +51,22 @@ public interface ParameterContext extends ParameterLookup, Authorizable { void setDescription(String description); /** - * Updates the Parameters within this context to match the given set of Parameters. - * @param updatedParameters the updated set of parameters + * Updates the Parameters within this context to match the given set of Parameters. If the Parameter Context contains any parameters that are not in + * the given set of updated Parameters, those parameters are unaffected. However, if the Map contains any key with a null value, the + * parameter whose name is given by the key will be removed + * + * @param updatedParameters the updated set of parameters, keyed by Parameter name * @throws IllegalStateException if any parameter is modified or removed and that parameter is being referenced by a running Processor or an enabled Controller Service, or if * an update would result in changing the sensitivity of any parameter */ - void setParameters(Set updatedParameters); + void setParameters(Map updatedParameters); /** * Ensures that it is legal to update the Parameters for this Parameter Context to match the given set of Parameters - * @param parameters the Set of Parameters that are to become the new Parameters for this Parameter Context + * @param parameters the updated set of parameters, keyed by Parameter name * @throws IllegalStateException if setting the given set of Parameters is not legal */ - void verifyCanSetParameters(Set parameters); + void verifyCanSetParameters(Map parameters); /** diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java index a2c8d4cfe7..6d970db9cf 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/StandardFlowSynchronizer.java @@ -131,6 +131,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.zip.GZIPInputStream; @@ -547,10 +548,10 @@ public class StandardFlowSynchronizer implements FlowSynchronizer { } private ParameterContext createParameterContext(final ParameterContextDTO dto, final FlowManager flowManager) { - final Set parameters = dto.getParameters().stream() + final Map parameters = dto.getParameters().stream() .map(ParameterEntity::getParameter) .map(this::createParameter) - .collect(Collectors.toSet()); + .collect(Collectors.toMap(param -> param.getDescriptor().getName(), Function.identity())); final ParameterContext context = flowManager.createParameterContext(dto.getId(), dto.getName(), parameters); context.setDescription(dto.getDescription()); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java index 1ec226e517..f2f1bc71ba 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/flow/StandardFlowManager.java @@ -736,7 +736,7 @@ public class StandardFlowManager implements FlowManager { } @Override - public ParameterContext createParameterContext(final String id, final String name, final Set parameters) { + public ParameterContext createParameterContext(final String id, final String name, final Map parameters) { final boolean namingConflict = parameterContextManager.getParameterContexts().stream() .anyMatch(paramContext -> paramContext.getName().equals(name)); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java index e5d12a5173..04cfb8ad78 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/parameter/StandardParameterContext.java @@ -34,7 +34,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -97,32 +96,26 @@ public class StandardParameterContext implements ParameterContext { return description; } - public void setParameters(final Set updatedParameters) { + public void setParameters(final Map updatedParameters) { writeLock.lock(); try { this.version++; - verifyCanSetParameters(updatedParameters); boolean changeAffectingComponents = false; - for (final Parameter parameter : updatedParameters) { - if (parameter.getValue() == null && parameter.getDescriptor().getDescription() == null) { - parameters.remove(parameter.getDescriptor()); - changeAffectingComponents = true; - } else if (parameter.getValue() == null) { - // Value is null but description is not. Just update the description of the existing Parameter. - final Parameter existingParameter = parameters.get(parameter.getDescriptor()); - final ParameterDescriptor existingDescriptor = existingParameter.getDescriptor(); - final ParameterDescriptor replacementDescriptor = new ParameterDescriptor.Builder() - .from(existingDescriptor) - .description(parameter.getDescriptor().getDescription()) - .build(); + for (final Map.Entry entry : updatedParameters.entrySet()) { + final String parameterName = entry.getKey(); + final Parameter parameter = entry.getValue(); - final Parameter replacementParameter = new Parameter(replacementDescriptor, existingParameter.getValue()); - parameters.put(parameter.getDescriptor(), replacementParameter); - } else { - parameters.put(parameter.getDescriptor(), parameter); + if (parameter == null) { + final ParameterDescriptor parameterDescriptor = new ParameterDescriptor.Builder().name(parameterName).build(); + parameters.remove(parameterDescriptor); changeAffectingComponents = true; + } else { + final Parameter oldParameter = parameters.put(parameter.getDescriptor(), parameter); + if (oldParameter == null || !Objects.equals(oldParameter.getValue(), parameter.getValue())) { + changeAffectingComponents = true; + } } } @@ -195,25 +188,23 @@ public class StandardParameterContext implements ParameterContext { } @Override - public void verifyCanSetParameters(final Set updatedParameters) { + public void verifyCanSetParameters(final Map updatedParameters) { // Ensure that the updated parameters will not result in changing the sensitivity flag of any parameter. - for (final Parameter updatedParameter : updatedParameters) { - validateSensitiveFlag(updatedParameter); - - // Parameters' names and sensitivity flags are immutable. However, the description and value are mutable. If both value and description are - // set to `null`, this is the indication that the Parameter should be removed. If the value is `null` but the Description is supplied, the user - // is indicating that only the description is to be changed. - if (updatedParameter.getValue() == null && updatedParameter.getDescriptor().getDescription() == null) { - validateReferencingComponents(updatedParameter, "remove"); - } else if (updatedParameter.getValue() != null) { - validateReferencingComponents(updatedParameter, "update"); - } else { - // Only parameter is changing. No value is set. This means that the Parameter must already exist. - final Optional existing = getParameter(updatedParameter.getDescriptor()); - if (!existing.isPresent()) { - throw new IllegalStateException("Cannot add Parameter '" + updatedParameter.getDescriptor().getName() + "' without providing a value"); - } + for (final Map.Entry entry : updatedParameters.entrySet()) { + final String parameterName = entry.getKey(); + final Parameter parameter = entry.getValue(); + if (parameter == null) { + // parameter is being deleted. + validateReferencingComponents(parameterName, null,"remove"); + continue; } + + if (!Objects.equals(parameterName, parameter.getDescriptor().getName())) { + throw new IllegalArgumentException("Parameter '" + parameterName + "' was specified with the wrong key in the Map"); + } + + validateSensitiveFlag(parameter); + validateReferencingComponents(parameterName, parameter, "update"); } } @@ -236,25 +227,27 @@ public class StandardParameterContext implements ParameterContext { } - private void validateReferencingComponents(final Parameter updatedParameter, final String parameterAction) { - final String paramName = updatedParameter.getDescriptor().getName(); - - for (final ProcessorNode procNode : parameterReferenceManager.getProcessorsReferencing(this, paramName)) { + private void validateReferencingComponents(final String parameterName, final Parameter parameter, final String parameterAction) { + for (final ProcessorNode procNode : parameterReferenceManager.getProcessorsReferencing(this, parameterName)) { if (procNode.isRunning()) { - throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + paramName + "' because it is referenced by " + procNode + ", which is currently running"); + throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + parameterName + "' because it is referenced by " + procNode + ", which is currently running"); } - validateParameterSensitivity(updatedParameter, procNode); + if (parameter != null) { + validateParameterSensitivity(parameter, procNode); + } } - for (final ControllerServiceNode serviceNode : parameterReferenceManager.getControllerServicesReferencing(this, paramName)) { + for (final ControllerServiceNode serviceNode : parameterReferenceManager.getControllerServicesReferencing(this, parameterName)) { final ControllerServiceState serviceState = serviceNode.getState(); if (serviceState != ControllerServiceState.DISABLED) { - throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + paramName + "' because it is referenced by " + throw new IllegalStateException("Cannot " + parameterAction + " parameter '" + parameterName + "' because it is referenced by " + serviceNode + ", which currently has a state of " + serviceState); } - validateParameterSensitivity(updatedParameter, serviceNode); + if (parameter != null) { + validateParameterSensitivity(parameter, serviceNode); + } } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java index b1a44e7f8d..3fc7e06d65 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/processor/StandardValidationContext.java @@ -33,6 +33,7 @@ import org.apache.nifi.controller.service.ControllerServiceProvider; import org.apache.nifi.controller.service.ControllerServiceState; import org.apache.nifi.expression.ExpressionLanguageCompiler; import org.apache.nifi.groups.ProcessGroup; +import org.apache.nifi.parameter.Parameter; import org.apache.nifi.parameter.ParameterContext; import org.apache.nifi.parameter.ParameterReference; import org.apache.nifi.registry.VariableRegistry; @@ -43,6 +44,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; @@ -216,6 +218,21 @@ public class StandardValidationContext implements ValidationContext { return parameterContext.getParameter(parameterName).isPresent(); } + @Override + public boolean isParameterSet(final String parameterName) { + if (parameterContext == null) { + return false; + } + + final Optional parameterOption = parameterContext.getParameter(parameterName); + if (!parameterOption.isPresent()) { + return false; + } + + final String value = parameterOption.get().getValue(); + return value != null; + } + @Override public String toString() { return "StandardValidationContext[componentId=" + componentId + ", properties=" + properties + "]"; diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java index 08dc4f3f0c..d1fb511587 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/TestStandardProcessorNode.java @@ -550,6 +550,11 @@ public class TestStandardProcessorNode { public boolean isParameterDefined(final String parameterName) { return false; } + + @Override + public boolean isParameterSet(final String parameterName) { + return false; + } }; } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/AlwaysAuthenticate.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/AlwaysAuthenticate.java new file mode 100644 index 0000000000..9640be5e78 --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/AlwaysAuthenticate.java @@ -0,0 +1,46 @@ +/* + * 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.integration.auth; + +import org.apache.nifi.authentication.AuthenticationResponse; +import org.apache.nifi.authentication.LoginCredentials; +import org.apache.nifi.authentication.LoginIdentityProvider; +import org.apache.nifi.authentication.LoginIdentityProviderConfigurationContext; +import org.apache.nifi.authentication.LoginIdentityProviderInitializationContext; +import org.apache.nifi.authentication.exception.IdentityAccessException; +import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException; +import org.apache.nifi.authentication.exception.ProviderCreationException; +import org.apache.nifi.authentication.exception.ProviderDestructionException; + +public class AlwaysAuthenticate implements LoginIdentityProvider { + @Override + public AuthenticationResponse authenticate(final LoginCredentials credentials) throws InvalidLoginCredentialsException, IdentityAccessException { + return new AuthenticationResponse(credentials.getUsername(), credentials.getUsername(), 1_000_000L, "unit-test-issuer"); + } + + @Override + public void initialize(final LoginIdentityProviderInitializationContext initializationContext) throws ProviderCreationException { + } + + @Override + public void onConfigured(final LoginIdentityProviderConfigurationContext configurationContext) throws ProviderCreationException { + } + + @Override + public void preDestruction() throws ProviderDestructionException { + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/VolatileAccessPolicyProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/VolatileAccessPolicyProvider.java new file mode 100644 index 0000000000..c010f0569b --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/VolatileAccessPolicyProvider.java @@ -0,0 +1,118 @@ +/* + * 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.integration.auth; + +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.AccessPolicyProvider; +import org.apache.nifi.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class VolatileAccessPolicyProvider implements AccessPolicyProvider { + private final VolatileUserGroupProvider userGroupProvider = new VolatileUserGroupProvider(); + private Set accessPolicies = new HashSet<>(); + + public synchronized void grantAccess(final String user, final String resourceIdentifier, final RequestAction action) { + final AccessPolicy existingPolicy = getAccessPolicy(resourceIdentifier, action); + + final AccessPolicy policy; + if (existingPolicy == null) { + policy = new AccessPolicy.Builder() + .addUser(user) + .action(action) + .identifierGenerateRandom() + .resource(resourceIdentifier) + .build(); + } else { + policy = new AccessPolicy.Builder() + .addUsers(existingPolicy.getUsers()) + .addUser(user) + .action(action) + .identifier(existingPolicy.getIdentifier()) + .resource(resourceIdentifier) + .build(); + } + + accessPolicies.remove(existingPolicy); + accessPolicies.add(policy); + } + + public synchronized void revokeAccess(final String user, final String resourceIdentifier, final RequestAction action) { + final AccessPolicy existingPolicy = getAccessPolicy(resourceIdentifier, action); + + if (existingPolicy == null) { + return; + } + + final AccessPolicy policy= new AccessPolicy.Builder() + .addUsers(existingPolicy.getUsers()) + .removeUser(user) + .action(action) + .identifier(existingPolicy.getIdentifier()) + .resource(resourceIdentifier) + .build(); + + accessPolicies.remove(existingPolicy); + accessPolicies.add(policy); + } + + @Override + public synchronized Set getAccessPolicies() throws AuthorizationAccessException { + return new HashSet<>(accessPolicies); + } + + @Override + public synchronized AccessPolicy getAccessPolicy(final String identifier) throws AuthorizationAccessException { + return accessPolicies.stream() + .filter(policy -> policy.getIdentifier().equals(identifier)) + .findAny() + .orElse(null); + } + + @Override + public synchronized AccessPolicy getAccessPolicy(final String resourceIdentifier, final RequestAction action) throws AuthorizationAccessException { + return accessPolicies.stream() + .filter(policy -> Objects.equals(policy.getResource(), resourceIdentifier)) + .filter(policy -> Objects.equals(policy.getAction(), action)) + .findAny() + .orElse(null); + } + + @Override + public synchronized VolatileUserGroupProvider getUserGroupProvider() { + return userGroupProvider; + } + + @Override + public void initialize(final AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/VolatileUserGroupProvider.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/VolatileUserGroupProvider.java new file mode 100644 index 0000000000..7e4849bcbe --- /dev/null +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/auth/VolatileUserGroupProvider.java @@ -0,0 +1,111 @@ +/* + * 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.integration.auth; + +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.User; +import org.apache.nifi.authorization.UserAndGroups; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class VolatileUserGroupProvider implements UserGroupProvider { + private final Map users = new HashMap<>(); + private final Map groups = new HashMap<>(); + private final Map> userGroupMapping = new HashMap<>(); + + public void addUser(final User user) { + this.users.put(user.getIdentifier(), user); + } + + public void addGroup(final Group group) { + this.groups.put(group.getIdentifier(), group); + } + + public void addUserToGroup(final User user, final Group group) { + userGroupMapping.computeIfAbsent(user, i -> new HashSet<>()).add(group); + } + + @Override + public Set getUsers() throws AuthorizationAccessException { + return new HashSet<>(users.values()); + } + + @Override + public User getUser(final String identifier) throws AuthorizationAccessException { + return users.get(identifier); + } + + @Override + public User getUserByIdentity(final String identity) throws AuthorizationAccessException { + return users.values().stream() + .filter(user -> Objects.equals(identity, user.getIdentity())) + .findFirst() + .orElse(null); + } + + @Override + public Set getGroups() throws AuthorizationAccessException { + return new HashSet<>(groups.values()); + } + + @Override + public Group getGroup(final String identifier) throws AuthorizationAccessException { + return groups.get(identifier); + } + + @Override + public UserAndGroups getUserAndGroups(final String identity) throws AuthorizationAccessException { + final User user = getUserByIdentity(identity); + final Set groups = userGroupMapping.get(user); + final Set groupCopy = groups == null ? Collections.emptySet() : new HashSet<>(groups); + + return new UserAndGroups() { + @Override + public User getUser() { + return user; + } + + @Override + public Set getGroups() { + return groupCopy; + } + }; + } + + @Override + public void initialize(final UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(final AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } +} diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/parameters/ParametersIT.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/parameters/ParametersIT.java index 7d0755da32..4a394ae518 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/parameters/ParametersIT.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/parameters/ParametersIT.java @@ -58,7 +58,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); updateAttribute.setProperties(Collections.singletonMap("test", "#{test}")); @@ -83,7 +83,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); updateAttribute.setProperties(Collections.singletonMap("test", "${#{test}:toUpper()}")); @@ -108,7 +108,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); updateAttribute.setProperties(Collections.singletonMap("test", "${#{test}:toUpper()}")); @@ -133,7 +133,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); @@ -165,7 +165,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); @@ -193,7 +193,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); @@ -225,7 +225,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); @@ -267,7 +267,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); @@ -307,7 +307,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); @@ -337,7 +337,7 @@ public class ParametersIT extends FrameworkIntegrationTest { final ParameterReferenceManager referenceManager = new StandardParameterReferenceManager(getFlowController().getFlowManager()); final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "param-context", referenceManager, null); - parameterContext.setParameters(Collections.singleton(new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); + parameterContext.setParameters(Collections.singletonMap("test", new Parameter(new ParameterDescriptor.Builder().name("test").build(), "unit"))); getRootGroup().setParameterContext(parameterContext); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/processor/ProcessorParameterTokenIT.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/processor/ProcessorParameterTokenIT.java index 3843d2aea7..906b092170 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/processor/ProcessorParameterTokenIT.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/processor/ProcessorParameterTokenIT.java @@ -40,7 +40,6 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -77,9 +76,9 @@ public class ProcessorParameterTokenIT extends FrameworkIntegrationTest { final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "testEscapedParameterReference", ParameterReferenceManager.EMPTY, null); getRootGroup().setParameterContext(parameterContext); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(new ParameterDescriptor.Builder().name("foo").build(), "bar")); - parameters.add(new Parameter(new ParameterDescriptor.Builder().name("sensitive").sensitive(true).build(), "*password*")); + final Map parameters = new HashMap<>(); + parameters.put("foo", new Parameter(new ParameterDescriptor.Builder().name("foo").build(), "bar")); + parameters.put("sensitive", new Parameter(new ParameterDescriptor.Builder().name("sensitive").sensitive(true).build(), "*password*")); parameterContext.setParameters(parameters); verifyText(procNode, "hello", "hello"); @@ -107,9 +106,9 @@ public class ProcessorParameterTokenIT extends FrameworkIntegrationTest { final ParameterContext parameterContext = new StandardParameterContext(UUID.randomUUID().toString(), "testEscapedParameterReference", ParameterReferenceManager.EMPTY, null); getRootGroup().setParameterContext(parameterContext); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(new ParameterDescriptor.Builder().name("foo").build(), "bar")); - parameters.add(new Parameter(new ParameterDescriptor.Builder().name("sensitive").sensitive(true).build(), "*password*")); + final Map parameters = new HashMap<>(); + parameters.put("foo", new Parameter(new ParameterDescriptor.Builder().name("foo").build(), "bar")); + parameters.put("sensitive", new Parameter(new ParameterDescriptor.Builder().name("sensitive").sensitive(true).build(), "*password*")); parameterContext.setParameters(parameters); verifyCannotSetParameter(procNode, "#{sensitive}", null); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/versioned/ImportFlowIT.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/versioned/ImportFlowIT.java index 52063150e7..99f91b8186 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/versioned/ImportFlowIT.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/integration/versioned/ImportFlowIT.java @@ -34,6 +34,8 @@ import org.apache.nifi.registry.flow.VersionedControllerService; import org.apache.nifi.registry.flow.VersionedFlow; import org.apache.nifi.registry.flow.VersionedFlowSnapshot; import org.apache.nifi.registry.flow.VersionedFlowSnapshotMetadata; +import org.apache.nifi.registry.flow.VersionedParameter; +import org.apache.nifi.registry.flow.VersionedParameterContext; import org.apache.nifi.registry.flow.VersionedProcessGroup; import org.apache.nifi.registry.flow.VersionedProcessor; import org.apache.nifi.registry.flow.mapping.NiFiRegistryFlowMapper; @@ -44,6 +46,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; @@ -70,7 +73,7 @@ public class ImportFlowIT extends FrameworkIntegrationTest { processor.setAutoTerminatedRelationships(Collections.singleton(REL_SUCCESS)); processor.setProperties(Collections.singletonMap(NopServiceReferencingProcessor.SERVICE.getName(), controllerService.getIdentifier())); - final VersionedFlowSnapshot proposedFlow = createFlowSnapshot(Collections.singletonList(controllerService), Collections.singletonList(processor)); + final VersionedFlowSnapshot proposedFlow = createFlowSnapshot(Collections.singletonList(controllerService), Collections.singletonList(processor), null); // Create an Inner Process Group and update it to match the Versioned Flow. final ProcessGroup innerGroup = getFlowController().getFlowManager().createProcessGroup("inner-group-id"); @@ -107,7 +110,7 @@ public class ImportFlowIT extends FrameworkIntegrationTest { } - private VersionedFlowSnapshot createFlowSnapshot(final List controllerServices, final List processors) { + private VersionedFlowSnapshot createFlowSnapshot(final List controllerServices, final List processors, final Map parameters) { final VersionedFlowSnapshotMetadata snapshotMetadata = new VersionedFlowSnapshotMetadata(); snapshotMetadata.setAuthor("unit-test"); snapshotMetadata.setBucketIdentifier("unit-test-bucket"); @@ -159,6 +162,21 @@ public class ImportFlowIT extends FrameworkIntegrationTest { versionedFlowSnapshot.setFlow(flow); versionedFlowSnapshot.setFlowContents(flowContents); + if (parameters != null) { + final Set versionedParameters = new HashSet<>(); + for (final Map.Entry entry : parameters.entrySet()) { + final VersionedParameter versionedParameter = new VersionedParameter(); + versionedParameter.setName(entry.getKey()); + versionedParameter.setValue(entry.getValue()); + versionedParameters.add(versionedParameter); + } + + final VersionedParameterContext versionedParameterContext = new VersionedParameterContext(); + versionedParameterContext.setName("Unit Test Context"); + versionedParameterContext.setParameters(versionedParameters); + versionedFlowSnapshot.setParameterContexts(Collections.singletonMap("unit-test-context", versionedParameterContext)); + } + return versionedFlowSnapshot; } } diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/parameter/TestStandardParameterContext.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/parameter/TestStandardParameterContext.java index 1b211a2d37..6008e636bc 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/parameter/TestStandardParameterContext.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/parameter/TestStandardParameterContext.java @@ -27,7 +27,6 @@ import org.mockito.Mockito; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -47,9 +46,9 @@ public class TestStandardParameterContext { final ParameterDescriptor xyzDescriptor = new ParameterDescriptor.Builder().name("xyz").build(); final ParameterDescriptor fooDescriptor = new ParameterDescriptor.Builder().name("foo").description("bar").sensitive(true).build(); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(abcDescriptor, "123")); - parameters.add(new Parameter(xyzDescriptor, "242526")); + final Map parameters = new HashMap<>(); + parameters.put("abc", new Parameter(abcDescriptor, "123")); + parameters.put("xyz", new Parameter(xyzDescriptor, "242526")); context.setParameters(parameters); @@ -63,15 +62,15 @@ public class TestStandardParameterContext { assertNull(xyzParam.getDescriptor().getDescription()); assertEquals("242526", xyzParam.getValue()); - final Set secondParameters = new HashSet<>(); - secondParameters.add(new Parameter(fooDescriptor, "baz")); + final Map secondParameters = new HashMap<>(); + secondParameters.put("foo", new Parameter(fooDescriptor, "baz")); context.setParameters(secondParameters); assertTrue(context.getParameter("abc").isPresent()); assertTrue(context.getParameter("xyz").isPresent()); - secondParameters.add(new Parameter(abcParam.getDescriptor(), null)); - secondParameters.add(new Parameter(xyzParam.getDescriptor(), null)); + secondParameters.put("abc", null); + secondParameters.put("xyz", null); context.setParameters(secondParameters); @@ -86,8 +85,8 @@ public class TestStandardParameterContext { assertEquals(Collections.singletonMap(fooDescriptor, fooParam), context.getParameters()); - final Set thirdParameters = new HashSet<>(); - thirdParameters.add(new Parameter(fooDescriptor, "other")); + final Map thirdParameters = new HashMap<>(); + thirdParameters.put("foo", new Parameter(fooDescriptor, "other")); context.setParameters(thirdParameters); assertEquals("other", context.getParameter("foo").get().getValue()); @@ -100,8 +99,8 @@ public class TestStandardParameterContext { final ParameterDescriptor abcDescriptor = new ParameterDescriptor.Builder().name("abc").description("abc").build(); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(abcDescriptor, "123")); + final Map parameters = new HashMap<>(); + parameters.put("abc", new Parameter(abcDescriptor, "123")); context.setParameters(parameters); @@ -112,7 +111,7 @@ public class TestStandardParameterContext { ParameterDescriptor updatedDescriptor = new ParameterDescriptor.Builder().name("abc").description("Updated").build(); final Parameter newDescriptionParam = new Parameter(updatedDescriptor, "321"); - context.setParameters(Collections.singleton(newDescriptionParam)); + context.setParameters(Collections.singletonMap("abc", newDescriptionParam)); abcParam = context.getParameter("abc").get(); assertEquals(abcDescriptor, abcParam.getDescriptor()); @@ -121,12 +120,12 @@ public class TestStandardParameterContext { updatedDescriptor = new ParameterDescriptor.Builder().name("abc").description("Updated Again").build(); final Parameter paramWithoutValue = new Parameter(updatedDescriptor, null); - context.setParameters(Collections.singleton(paramWithoutValue)); + context.setParameters(Collections.singletonMap("abc", paramWithoutValue)); abcParam = context.getParameter("abc").get(); assertEquals(abcDescriptor, abcParam.getDescriptor()); assertEquals("Updated Again", abcParam.getDescriptor().getDescription()); - assertEquals("321", abcParam.getValue()); + assertNull(abcParam.getValue()); } @Test @@ -139,17 +138,17 @@ public class TestStandardParameterContext { final ParameterDescriptor xyzDescriptor = new ParameterDescriptor.Builder().name("xyz").build(); final ParameterDescriptor fooDescriptor = new ParameterDescriptor.Builder().name("foo").description("bar").sensitive(true).build(); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(abcDescriptor, "123")); - parameters.add(new Parameter(xyzDescriptor, "242526")); + final Map parameters = new HashMap<>(); + parameters.put("abc", new Parameter(abcDescriptor, "123")); + parameters.put("xyz", new Parameter(xyzDescriptor, "242526")); context.setParameters(parameters); final ParameterDescriptor sensitiveXyzDescriptor = new ParameterDescriptor.Builder().name("xyz").sensitive(true).build(); - final Set updatedParameters = new HashSet<>(); - updatedParameters.add(new Parameter(fooDescriptor, "baz")); - updatedParameters.add(new Parameter(sensitiveXyzDescriptor, "242526")); + final Map updatedParameters = new HashMap<>(); + updatedParameters.put("foo", new Parameter(fooDescriptor, "baz")); + updatedParameters.put("xyz", new Parameter(sensitiveXyzDescriptor, "242526")); try { context.setParameters(updatedParameters); @@ -159,7 +158,7 @@ public class TestStandardParameterContext { final ParameterDescriptor insensitiveAbcDescriptor = new ParameterDescriptor.Builder().name("abc").sensitive(false).build(); updatedParameters.clear(); - updatedParameters.add(new Parameter(insensitiveAbcDescriptor, "123")); + updatedParameters.put("abc", new Parameter(insensitiveAbcDescriptor, "123")); try { context.setParameters(updatedParameters); @@ -179,13 +178,13 @@ public class TestStandardParameterContext { final ParameterDescriptor abcDescriptor = new ParameterDescriptor.Builder().name("abc").sensitive(true).build(); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(abcDescriptor, "123")); + final Map parameters = new HashMap<>(); + parameters.put("abc", new Parameter(abcDescriptor, "123")); context.setParameters(parameters); parameters.clear(); - parameters.add(new Parameter(abcDescriptor, "321")); + parameters.put("abc", new Parameter(abcDescriptor, "321")); context.setParameters(parameters); assertEquals("321", context.getParameter("abc").get().getValue()); @@ -194,7 +193,7 @@ public class TestStandardParameterContext { Mockito.when(procNode.isRunning()).thenReturn(true); parameters.clear(); - parameters.add(new Parameter(abcDescriptor, "123")); + parameters.put("abc", new Parameter(abcDescriptor, "123")); try { context.setParameters(parameters); @@ -202,10 +201,10 @@ public class TestStandardParameterContext { } catch (final IllegalStateException expected) { } - context.setParameters(Collections.emptySet()); + context.setParameters(Collections.emptyMap()); parameters.clear(); - parameters.add(new Parameter(abcDescriptor, null)); + parameters.put("abc", new Parameter(abcDescriptor, null)); try { context.setParameters(parameters); Assert.fail("Was able to remove parameter while referencing processor was running"); @@ -224,15 +223,15 @@ public class TestStandardParameterContext { Mockito.when(serviceNode.getState()).thenReturn(ControllerServiceState.ENABLED); final ParameterDescriptor abcDescriptor = new ParameterDescriptor.Builder().name("abc").sensitive(true).build(); - final Set parameters = new HashSet<>(); - parameters.add(new Parameter(abcDescriptor, "123")); + final Map parameters = new HashMap<>(); + parameters.put("abc", new Parameter(abcDescriptor, "123")); context.setParameters(parameters); referenceManager.addControllerServiceReference("abc", serviceNode); parameters.clear(); - parameters.add(new Parameter(abcDescriptor, "321")); + parameters.put("abc", new Parameter(abcDescriptor, "321")); for (final ControllerServiceState state : EnumSet.of(ControllerServiceState.ENABLED, ControllerServiceState.ENABLING, ControllerServiceState.DISABLING)) { Mockito.when(serviceNode.getState()).thenReturn(state); @@ -249,7 +248,7 @@ public class TestStandardParameterContext { parameters.clear(); context.setParameters(parameters); - parameters.add(new Parameter(abcDescriptor, null)); + parameters.put("abc", new Parameter(abcDescriptor, null)); try { context.setParameters(parameters); Assert.fail("Was able to remove parameter being referenced by Controller Service that is DISABLING"); diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/int-tests/clustered-nifi.properties b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/int-tests/clustered-nifi.properties index 2516b42364..809eaf3359 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/int-tests/clustered-nifi.properties +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/resources/int-tests/clustered-nifi.properties @@ -218,7 +218,7 @@ nifi.cluster.flow.election.max.candidates= # cluster load balancing properties # nifi.cluster.load.balance.host= -nifi.cluster.load.balance.port=6342 +nifi.cluster.load.balance.port=0 nifi.cluster.load.balance.connections.per.node=4 nifi.cluster.load.balance.max.thread.count=8 nifi.cluster.load.balance.comms.timeout=30 sec 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 34670e0681..7594fa8622 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 @@ -1046,10 +1046,11 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { final Set boundProcessGroups = parameterContext.getParameterReferenceManager().getProcessGroupsBound(parameterContext); final ParameterContext updatedParameterContext = new StandardParameterContext(parameterContext.getIdentifier(), parameterContext.getName(), ParameterReferenceManager.EMPTY, null); - final Set parameters = parameterContextDto.getParameters().stream() + final Map parameters = new HashMap<>(); + parameterContextDto.getParameters().stream() .map(ParameterEntity::getParameter) .map(this::createParameter) - .collect(Collectors.toSet()); + .forEach(param -> parameters.put(param.getDescriptor().getName(), param)); updatedParameterContext.setParameters(parameters); final List validationResults = new ArrayList<>(); @@ -1089,6 +1090,10 @@ public class StandardNiFiServiceFacade implements NiFiServiceFacade { } private Parameter createParameter(final ParameterDTO dto) { + if (dto.getDescription() == null && dto.getSensitive() == null && dto.getValue() == null) { + return null; // null description, sensitivity flag, and value indicates a deletion, which we want to represent as a null Parameter. + } + final ParameterDescriptor descriptor = new ParameterDescriptor.Builder() .name(dto.getName()) .description(dto.getDescription()) diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java index aaab83045e..4b02b9d661 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/dao/impl/StandardParameterContextDAO.java @@ -37,10 +37,10 @@ import org.apache.nifi.web.api.entity.ParameterEntity; import org.apache.nifi.web.dao.ParameterContextDAO; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.stream.Collectors; public class StandardParameterContextDAO implements ParameterContextDAO { private FlowManager flowManager; @@ -57,7 +57,7 @@ public class StandardParameterContextDAO implements ParameterContextDAO { @Override public ParameterContext createParameterContext(final ParameterContextDTO parameterContextDto) { - final Set parameters = getParameters(parameterContextDto); + final Map parameters = getParameters(parameterContextDto); final ParameterContext parameterContext = flowManager.createParameterContext(parameterContextDto.getId(), parameterContextDto.getName(), parameters); if (parameterContextDto.getDescription() != null) { parameterContext.setDescription(parameterContextDto.getDescription()); @@ -65,16 +65,30 @@ public class StandardParameterContextDAO implements ParameterContextDAO { return parameterContext; } - private Set getParameters(final ParameterContextDTO parameterContextDto) { - final Set parameterDtos = parameterContextDto.getParameters(); - if (parameterDtos == null) { - return Collections.emptySet(); + private Map getParameters(final ParameterContextDTO parameterContextDto) { + final Set parameterEntities = parameterContextDto.getParameters(); + if (parameterEntities == null) { + return Collections.emptyMap(); } - return parameterContextDto.getParameters().stream() - .map(ParameterEntity::getParameter) - .map(this::createParameter) - .collect(Collectors.toSet()); + final Map parameterMap = new HashMap<>(); + for (final ParameterEntity parameterEntity : parameterEntities) { + final ParameterDTO parameterDto = parameterEntity.getParameter(); + + if (parameterDto.getName() == null) { + throw new IllegalArgumentException("Cannot specify a Parameter without a name"); + } + + final boolean deletion = parameterDto.getDescription() == null && parameterDto.getSensitive() == null && parameterDto.getValue() == null; + if (deletion) { + parameterMap.put(parameterDto.getName(), null); + } else { + final Parameter parameter = createParameter(parameterDto); + parameterMap.put(parameterDto.getName(), parameter); + } + } + + return parameterMap; } private Parameter createParameter(final ParameterDTO dto) { @@ -118,7 +132,7 @@ public class StandardParameterContextDAO implements ParameterContextDAO { } if (parameterContextDto.getParameters() != null) { - final Set parameters = getParameters(parameterContextDto); + final Map parameters = getParameters(parameterContextDto); context.setParameters(parameters); } diff --git a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/ValidationContextAdapter.java b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/ValidationContextAdapter.java index b983c16f5d..eee7e37370 100644 --- a/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/ValidationContextAdapter.java +++ b/nifi-nar-bundles/nifi-scripting-bundle/nifi-scripting-processors/src/main/java/org/apache/nifi/script/impl/ValidationContextAdapter.java @@ -100,6 +100,11 @@ public abstract class ValidationContextAdapter implements ValidationContext { return innerValidationContext.isParameterDefined(parameterName); } + @Override + public boolean isParameterSet(final String parameterName) { + return innerValidationContext.isParameterSet(parameterName); + } + @Override public Collection getReferencedParameters(final String propertyName) { return innerValidationContext.getReferencedParameters(propertyName); diff --git a/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessParameterContext.java b/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessParameterContext.java index e48a965274..2a9ad4fdbe 100644 --- a/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessParameterContext.java +++ b/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessParameterContext.java @@ -61,13 +61,13 @@ public class StatelessParameterContext implements ParameterContext { } @Override - public void setParameters(final Set updatedParameters) { - throw new UnsupportedOperationException(); + public void setParameters(final Map updatedParameters) { + throw new UnsupportedOperationException(); // This parameter context does not support updating - all parameters are provided in the constructor. } @Override - public void verifyCanSetParameters(final Set parameters) { - throw new UnsupportedOperationException(); + public void verifyCanSetParameters(final Map parameters) { + throw new UnsupportedOperationException(); // This parameter context does not support updating - all parameters are provided in the constructor. } @Override diff --git a/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessValidationContext.java b/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessValidationContext.java index 81f1eb0a93..9ed84344b4 100644 --- a/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessValidationContext.java +++ b/nifi-stateless/nifi-stateless-core/src/main/java/org/apache/nifi/stateless/core/StatelessValidationContext.java @@ -26,6 +26,7 @@ import org.apache.nifi.controller.ControllerService; import org.apache.nifi.controller.ControllerServiceLookup; import org.apache.nifi.controller.PropertyConfiguration; import org.apache.nifi.expression.ExpressionLanguageCompiler; +import org.apache.nifi.parameter.Parameter; import org.apache.nifi.parameter.ParameterContext; import org.apache.nifi.parameter.ParameterReference; import org.apache.nifi.registry.VariableRegistry; @@ -36,6 +37,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; public class StatelessValidationContext implements ValidationContext { @@ -146,6 +148,22 @@ public class StatelessValidationContext implements ValidationContext { return parameterContext.getParameter(parameterName).isPresent(); } + @Override + public boolean isParameterSet(final String parameterName) { + if (parameterContext == null) { + return false; + } + + final Optional parameterOption = parameterContext.getParameter(parameterName); + if (!parameterOption.isPresent()) { + return false; + } + + final String value = parameterOption.get().getValue(); + return value != null; + + } + @Override public ControllerServiceLookup getControllerServiceLookup() { return this.lookup;