This is an automated email from the ASF dual-hosted git repository.

turcsanyi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 6bd480f6ce NIFI-15200 - Introduce Authentication Strategy for GCP 
Credentials Controller Service
6bd480f6ce is described below

commit 6bd480f6cee4564516dcc3337156f3e38c019dc8
Author: Pierre Villard <[email protected]>
AuthorDate: Mon Nov 10 12:47:06 2025 +0100

    NIFI-15200 - Introduce Authentication Strategy for GCP Credentials 
Controller Service
    
    This closes #10510.
    
    Signed-off-by: Peter Turcsanyi <[email protected]>
---
 .../factory/AuthenticationStrategy.java            |  65 ++++++++++
 .../factory/CredentialPropertyDescriptors.java     |  36 +++---
 .../credentials/factory/CredentialsFactory.java    |  61 +++-------
 .../credentials/factory/CredentialsStrategy.java   |  19 ---
 .../AbstractBooleanCredentialsStrategy.java        |  69 -----------
 .../strategies/AbstractCredentialsStrategy.java    |  51 +-------
 .../AbstractServiceAccountCredentialsStrategy.java |   4 +-
 ... => ApplicationDefaultCredentialsStrategy.java} |  15 +--
 .../ComputeEngineCredentialsStrategy.java          |   5 +-
 ...licitApplicationDefaultCredentialsStrategy.java |  46 -------
 .../JsonFileServiceAccountCredentialsStrategy.java |   4 +-
 ...sonStringServiceAccountCredentialsStrategy.java |   4 +-
 .../service/GCPCredentialsControllerService.java   |  55 +++++++--
 .../processors/gcp/bigquery/PutBigQueryIT.java     |   4 +
 .../factory/CredentialsFactoryTest.java            |  24 ++--
 .../factory/MockCredentialsFactoryProcessor.java   |  17 +--
 ...PCredentialsControllerServiceMigrationTest.java | 133 +++++++++++++++++++++
 .../service/GCPCredentialsServiceTest.java         |  18 +--
 18 files changed, 324 insertions(+), 306 deletions(-)

diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/AuthenticationStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/AuthenticationStrategy.java
new file mode 100644
index 0000000000..7dcaa2941e
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/AuthenticationStrategy.java
@@ -0,0 +1,65 @@
+/*
+ * 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.processors.gcp.credentials.factory;
+
+import org.apache.nifi.components.DescribedValue;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+/**
+ * Supported authentication strategies for configuring GCP credentials.
+ */
+public enum AuthenticationStrategy implements DescribedValue {
+    APPLICATION_DEFAULT("Application Default Credentials", "Use Google 
Application Default Credentials such as the GOOGLE_APPLICATION_CREDENTIALS 
environment variable or gcloud configuration."),
+    SERVICE_ACCOUNT_JSON_FILE("Service Account Credentials (Json File)", "Use 
a Service Account key stored in a JSON file."),
+    SERVICE_ACCOUNT_JSON("Service Account Credentials (Json Value)", "Use a 
Service Account key provided directly as JSON."),
+    COMPUTE_ENGINE("Compute Engine Credentials", "Use the Compute Engine 
service account available to the NiFi instance.");
+
+    private final String displayName;
+    private final String description;
+
+    AuthenticationStrategy(final String displayName, final String description) 
{
+        this.displayName = displayName;
+        this.description = description;
+    }
+
+    @Override
+    public String getValue() {
+        return displayName;
+    }
+
+    @Override
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    @Override
+    public String getDescription() {
+        return description;
+    }
+
+    public static Optional<AuthenticationStrategy> fromValue(final String 
value) {
+        if (value == null) {
+            return Optional.empty();
+        }
+
+        return Arrays.stream(values())
+                .filter(strategy -> strategy.getValue().equals(value))
+                .findFirst();
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialPropertyDescriptors.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialPropertyDescriptors.java
index eadc9af773..6a87648f88 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialPropertyDescriptors.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialPropertyDescriptors.java
@@ -30,6 +30,14 @@ public final class CredentialPropertyDescriptors {
 
     private CredentialPropertyDescriptors() { }
 
+    public static final PropertyDescriptor AUTHENTICATION_STRATEGY = new 
PropertyDescriptor.Builder()
+            .name("Authentication Strategy")
+            .required(true)
+            .allowableValues(AuthenticationStrategy.class)
+            
.defaultValue(AuthenticationStrategy.APPLICATION_DEFAULT.getValue())
+            .description("Specifies how NiFi authenticates to Google Cloud. 
Depending on the strategy, additional properties might be required.")
+            .build();
+
     /**
      * Specifies use of Application Default Credentials
      *
@@ -37,30 +45,12 @@ public final class CredentialPropertyDescriptors {
      *     Google Application Default Credentials
      *     </a>
      */
-    public static final PropertyDescriptor USE_APPLICATION_DEFAULT_CREDENTIALS 
= new PropertyDescriptor.Builder()
+    public static final PropertyDescriptor 
LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS = new PropertyDescriptor.Builder()
             .name("Use Application Default Credentials")
-            .expressionLanguageSupported(ExpressionLanguageScope.NONE)
-            .required(false)
-            .addValidator(StandardValidators.BOOLEAN_VALIDATOR)
-            .sensitive(false)
-            .allowableValues("true", "false")
-            .defaultValue("false")
-            .description("If true, uses Google Application Default 
Credentials, which checks the " +
-                    "GOOGLE_APPLICATION_CREDENTIALS environment variable for a 
filepath to a service account JSON " +
-                    "key, the config generated by the gcloud sdk, the App 
Engine service account, and the Compute" +
-                    " Engine service account.")
             .build();
 
-    public static final PropertyDescriptor USE_COMPUTE_ENGINE_CREDENTIALS = 
new PropertyDescriptor.Builder()
+    public static final PropertyDescriptor 
LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS = new PropertyDescriptor.Builder()
             .name("Use Compute Engine Credentials")
-            .expressionLanguageSupported(ExpressionLanguageScope.NONE)
-            .required(false)
-            .addValidator(StandardValidators.BOOLEAN_VALIDATOR)
-            .sensitive(false)
-            .allowableValues("true", "false")
-            .defaultValue("false")
-            .description("If true, uses Google Compute Engine Credentials of 
the Compute Engine VM Instance " +
-                    "which NiFi is running on.")
             .build();
 
     /**
@@ -73,16 +63,18 @@ public final class CredentialPropertyDescriptors {
     public static final PropertyDescriptor SERVICE_ACCOUNT_JSON_FILE = new 
PropertyDescriptor.Builder()
             .name("Service Account JSON File")
             .expressionLanguageSupported(ExpressionLanguageScope.NONE)
-            .required(false)
+            .required(true)
             .identifiesExternalResource(ResourceCardinality.SINGLE, 
ResourceType.FILE)
+            .dependsOn(AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue())
             .description("Path to a file containing a Service Account key file 
in JSON format.")
             .build();
 
     public static final PropertyDescriptor SERVICE_ACCOUNT_JSON = new 
PropertyDescriptor.Builder()
             .name("Service Account JSON")
             .expressionLanguageSupported(ExpressionLanguageScope.ENVIRONMENT)
-            .required(false)
+            .required(true)
             .addValidator(JsonValidator.INSTANCE)
+            .dependsOn(AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON.getValue())
             .description("The raw JSON containing a Service Account keyfile.")
             .sensitive(true)
             .build();
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactory.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactory.java
index 9aaf9c2774..fb186fea3d 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactory.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactory.java
@@ -19,19 +19,15 @@ package org.apache.nifi.processors.gcp.credentials.factory;
 import com.google.auth.http.HttpTransportFactory;
 import com.google.auth.oauth2.GoogleCredentials;
 import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.components.ValidationResult;
+import 
org.apache.nifi.processors.gcp.credentials.factory.strategies.ApplicationDefaultCredentialsStrategy;
 import 
org.apache.nifi.processors.gcp.credentials.factory.strategies.ComputeEngineCredentialsStrategy;
-import 
org.apache.nifi.processors.gcp.credentials.factory.strategies.ExplicitApplicationDefaultCredentialsStrategy;
-import 
org.apache.nifi.processors.gcp.credentials.factory.strategies.ImplicitApplicationDefaultCredentialsStrategy;
 import 
org.apache.nifi.processors.gcp.credentials.factory.strategies.JsonFileServiceAccountCredentialsStrategy;
 import 
org.apache.nifi.processors.gcp.credentials.factory.strategies.JsonStringServiceAccountCredentialsStrategy;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.EnumMap;
 import java.util.Map;
+import java.util.Optional;
 
 /**
  * Generates GCP credentials in the form of GoogleCredential implementations 
for processors
@@ -45,50 +41,20 @@ import java.util.Map;
  */
 public class CredentialsFactory {
 
-    private final List<CredentialsStrategy> strategies = new ArrayList<>();
+    private final Map<AuthenticationStrategy, CredentialsStrategy> 
strategiesByAuthentication = new EnumMap<>(AuthenticationStrategy.class);
 
     public CredentialsFactory() {
-        // Primary Credential Strategies
-        strategies.add(new ExplicitApplicationDefaultCredentialsStrategy());
-        strategies.add(new JsonFileServiceAccountCredentialsStrategy());
-        strategies.add(new JsonStringServiceAccountCredentialsStrategy());
-        strategies.add(new ComputeEngineCredentialsStrategy());
-
-        // Implicit Default is the catch-all primary strategy
-        strategies.add(new ImplicitApplicationDefaultCredentialsStrategy());
+        
strategiesByAuthentication.put(AuthenticationStrategy.APPLICATION_DEFAULT, new 
ApplicationDefaultCredentialsStrategy());
+        
strategiesByAuthentication.put(AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE,
 new JsonFileServiceAccountCredentialsStrategy());
+        
strategiesByAuthentication.put(AuthenticationStrategy.SERVICE_ACCOUNT_JSON, new 
JsonStringServiceAccountCredentialsStrategy());
+        strategiesByAuthentication.put(AuthenticationStrategy.COMPUTE_ENGINE, 
new ComputeEngineCredentialsStrategy());
     }
 
     public CredentialsStrategy selectPrimaryStrategy(final 
Map<PropertyDescriptor, String> properties) {
-        for (CredentialsStrategy strategy : strategies) {
-            if (strategy.canCreatePrimaryCredential(properties)) {
-                return strategy;
-            }
-        }
-        return null;
-    }
-
-    public CredentialsStrategy selectPrimaryStrategy(final ValidationContext 
validationContext) {
-        final Map<PropertyDescriptor, String> properties = 
validationContext.getProperties();
-        return selectPrimaryStrategy(properties);
-    }
-
-    /**
-     * Validates GCP credential properties against the configured strategies 
to report any validation errors.
-     * @return Validation errors
-     */
-    public Collection<ValidationResult> validate(final ValidationContext 
validationContext) {
-        final CredentialsStrategy selectedStrategy = 
selectPrimaryStrategy(validationContext);
-        final List<ValidationResult> validationFailureResults = new 
ArrayList<>();
-
-        for (CredentialsStrategy strategy : strategies) {
-            final Collection<ValidationResult> strategyValidationFailures = 
strategy.validate(validationContext,
-                    selectedStrategy);
-            if (strategyValidationFailures != null) {
-                validationFailureResults.addAll(strategyValidationFailures);
-            }
-        }
-
-        return validationFailureResults;
+        final String authenticationStrategyValue = 
properties.get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY);
+        final Optional<AuthenticationStrategy> authenticationStrategy = 
AuthenticationStrategy.fromValue(authenticationStrategyValue);
+        return authenticationStrategy.map(strategiesByAuthentication::get)
+                
.orElse(strategiesByAuthentication.get(AuthenticationStrategy.APPLICATION_DEFAULT));
     }
 
     /**
@@ -100,6 +66,9 @@ public class CredentialsFactory {
      */
     public GoogleCredentials getGoogleCredentials(final 
Map<PropertyDescriptor, String> properties, final HttpTransportFactory 
transportFactory) throws IOException {
         final CredentialsStrategy primaryStrategy = 
selectPrimaryStrategy(properties);
+        if (primaryStrategy == null) {
+            throw new IllegalStateException("No matching authentication 
strategy is configured");
+        }
         return primaryStrategy.getGoogleCredentials(properties, 
transportFactory);
     }
 }
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsStrategy.java
index 1765e14ecb..63b780b50a 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsStrategy.java
@@ -19,11 +19,8 @@ package org.apache.nifi.processors.gcp.credentials.factory;
 import com.google.auth.http.HttpTransportFactory;
 import com.google.auth.oauth2.GoogleCredentials;
 import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.components.ValidationResult;
 
 import java.io.IOException;
-import java.util.Collection;
 import java.util.Map;
 
 /**
@@ -39,22 +36,6 @@ public interface CredentialsStrategy {
      */
     String getName();
 
-    /**
-     * Determines if this strategy can create primary credentials using the 
given properties.
-     * @return true if primary credentials can be created
-     */
-    boolean canCreatePrimaryCredential(Map<PropertyDescriptor, String> 
properties);
-
-
-    /**
-     * Validates the properties belonging to this strategy, given the selected 
primary strategy.  Errors may result
-     * from individually malformed properties, invalid combinations of 
properties, or inappropriate use of properties
-     * not consistent with the primary strategy.
-     * @param primaryStrategy the prevailing primary strategy
-     * @return validation errors
-     */
-    Collection<ValidationResult> validate(ValidationContext validationContext, 
CredentialsStrategy primaryStrategy);
-
     /**
      * Creates an AuthCredentials instance for this strategy, given the 
properties defined by the user.
      * @param transportFactory Sub-classes should utilize this transport 
factory
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractBooleanCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractBooleanCredentialsStrategy.java
deleted file mode 100644
index 7ed3eb33a6..0000000000
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractBooleanCredentialsStrategy.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.processors.gcp.credentials.factory.strategies;
-
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.components.ValidationResult;
-import org.apache.nifi.processors.gcp.credentials.factory.CredentialsStrategy;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-
-
-/**
- * Partial implementation of CredentialsStrategy to provide support for 
credential strategies specified by
- * a single boolean property.
- */
-public abstract class AbstractBooleanCredentialsStrategy extends 
AbstractCredentialsStrategy {
-
-    private PropertyDescriptor strategyProperty;
-
-    public AbstractBooleanCredentialsStrategy(String name, PropertyDescriptor 
strategyProperty) {
-        super(name, new PropertyDescriptor[]{
-                strategyProperty
-        });
-        this.strategyProperty = strategyProperty;
-    }
-
-    @Override
-    public boolean canCreatePrimaryCredential(Map<PropertyDescriptor, String> 
properties) {
-        return (properties.get(this.strategyProperty) != null
-                && 
properties.get(this.strategyProperty).equalsIgnoreCase("true"));
-    }
-
-    @Override
-    public Collection<ValidationResult> validate(final ValidationContext 
validationContext,
-                                                 final CredentialsStrategy 
primaryStrategy) {
-        boolean thisIsSelectedStrategy = this == primaryStrategy;
-        Boolean useStrategy = 
validationContext.getProperty(strategyProperty).asBoolean();
-        if (!thisIsSelectedStrategy && (useStrategy == null ? false : 
useStrategy)) {
-            String failureFormat = "property %1$s cannot be used with %2$s";
-            Collection<ValidationResult> validationFailureResults = new 
ArrayList<>();
-            String message = String.format(failureFormat, 
strategyProperty.getDisplayName(),
-                    primaryStrategy.getName());
-            validationFailureResults.add(new ValidationResult.Builder()
-                    .subject(strategyProperty.getDisplayName())
-                    .valid(false)
-                    .explanation(message).build());
-            return  validationFailureResults;
-        }
-        return null;
-    }
-
-}
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractCredentialsStrategy.java
index 44299dcc67..b80403e74e 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractCredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractCredentialsStrategy.java
@@ -16,65 +16,16 @@
  */
 package org.apache.nifi.processors.gcp.credentials.factory.strategies;
 
-import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.processors.gcp.credentials.factory.CredentialsStrategy;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-
 /**
  * Partial implementation of CredentialsStrategy to support most simple 
property-based strategies.
  */
 public abstract class AbstractCredentialsStrategy implements 
CredentialsStrategy {
     private final String name;
-    private final PropertyDescriptor[] requiredProperties;
 
-    public AbstractCredentialsStrategy(String name, PropertyDescriptor[] 
requiredProperties) {
+    public AbstractCredentialsStrategy(String name) {
         this.name = name;
-        this.requiredProperties = requiredProperties;
-    }
-
-    @Override
-    public boolean canCreatePrimaryCredential(Map<PropertyDescriptor, String> 
properties) {
-        for (PropertyDescriptor requiredProperty : requiredProperties) {
-            boolean containsRequiredProperty = 
properties.containsKey(requiredProperty);
-            String propertyValue = properties.get(requiredProperty);
-            boolean containsValue = propertyValue != null;
-            if (!containsRequiredProperty || !containsValue) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    @Override
-    public Collection<ValidationResult> validate(final ValidationContext 
validationContext,
-                                                 final CredentialsStrategy 
primaryStrategy) {
-        boolean thisIsSelectedStrategy = this == primaryStrategy;
-        String requiredMessageFormat = "property %1$s must be set with %2$s";
-        String excludedMessageFormat = "property %1$s cannot be used with 
%2$s";
-        String failureFormat = thisIsSelectedStrategy ? requiredMessageFormat 
: excludedMessageFormat;
-        Collection<ValidationResult> validationFailureResults = null;
-
-        for (PropertyDescriptor requiredProperty : requiredProperties) {
-            boolean requiredPropertyIsSet = 
validationContext.getProperty(requiredProperty).isSet();
-            if (requiredPropertyIsSet != thisIsSelectedStrategy) {
-                String message = String.format(failureFormat, 
requiredProperty.getDisplayName(),
-                        primaryStrategy.getName());
-                if (validationFailureResults == null) {
-                    validationFailureResults = new ArrayList<>();
-                }
-                validationFailureResults.add(new ValidationResult.Builder()
-                        .subject(requiredProperty.getDisplayName())
-                        .valid(false)
-                        .explanation(message).build());
-            }
-        }
-
-        return validationFailureResults;
     }
 
     @Override
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractServiceAccountCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractServiceAccountCredentialsStrategy.java
index 5bd3edbfcf..f8b6a9b8cc 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractServiceAccountCredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/AbstractServiceAccountCredentialsStrategy.java
@@ -31,8 +31,8 @@ import java.util.Map;
  * or through a flat JSON file.
  */
 public abstract class AbstractServiceAccountCredentialsStrategy extends 
AbstractCredentialsStrategy {
-    public AbstractServiceAccountCredentialsStrategy(String name, 
PropertyDescriptor[] requiredProperties) {
-        super(name, requiredProperties);
+    public AbstractServiceAccountCredentialsStrategy(String name) {
+        super(name);
     }
 
     protected abstract InputStream 
getServiceAccountJson(Map<PropertyDescriptor, String> properties) throws 
IOException;
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ImplicitApplicationDefaultCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ApplicationDefaultCredentialsStrategy.java
similarity index 71%
rename from 
nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ImplicitApplicationDefaultCredentialsStrategy.java
rename to 
nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ApplicationDefaultCredentialsStrategy.java
index e0535b0e5c..b51309ca1b 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ImplicitApplicationDefaultCredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ApplicationDefaultCredentialsStrategy.java
@@ -23,16 +23,17 @@ import org.apache.nifi.components.PropertyDescriptor;
 import java.io.IOException;
 import java.util.Map;
 
+
 /**
- * Supports Google Cloud Application Default Credentials.
- * Compared to ExplicitApplicationDefaultCredentialsStrategy, this strategy is 
always
- * willing to provide primary credentials, regardless of user input.  It is 
intended to be used as an invisible
- * fallback or default strategy.
+ * Supports GCP Application Default Credentials.
+ *
+ * @see <a 
href="https://developers.google.com/identity/protocols/application-default-credentials";>
+ *     Application Default Credentials</a>
  */
-public class ImplicitApplicationDefaultCredentialsStrategy extends 
AbstractCredentialsStrategy {
+public class ApplicationDefaultCredentialsStrategy extends 
AbstractCredentialsStrategy {
 
-    public ImplicitApplicationDefaultCredentialsStrategy() {
-        super("Application Default Credentials", new PropertyDescriptor[]{});
+    public ApplicationDefaultCredentialsStrategy() {
+        super("Application Default Credentials");
     }
 
     @Override
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ComputeEngineCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ComputeEngineCredentialsStrategy.java
index e98fb33bf2..a1eefb0b68 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ComputeEngineCredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ComputeEngineCredentialsStrategy.java
@@ -20,7 +20,6 @@ import com.google.auth.http.HttpTransportFactory;
 import com.google.auth.oauth2.ComputeEngineCredentials;
 import com.google.auth.oauth2.GoogleCredentials;
 import org.apache.nifi.components.PropertyDescriptor;
-import 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors;
 
 import java.io.IOException;
 import java.util.Map;
@@ -31,9 +30,9 @@ import java.util.Map;
  * @see <a 
href="https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances";>
  *     Service Accounts for Instances</a>
  */
-public class ComputeEngineCredentialsStrategy extends 
AbstractBooleanCredentialsStrategy {
+public class ComputeEngineCredentialsStrategy extends 
AbstractCredentialsStrategy {
     public ComputeEngineCredentialsStrategy() {
-        super("Compute Engine Credentials", 
CredentialPropertyDescriptors.USE_COMPUTE_ENGINE_CREDENTIALS);
+        super("Compute Engine Credentials");
     }
 
     @Override
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ExplicitApplicationDefaultCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ExplicitApplicationDefaultCredentialsStrategy.java
deleted file mode 100644
index 213d0b7f42..0000000000
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/ExplicitApplicationDefaultCredentialsStrategy.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.nifi.processors.gcp.credentials.factory.strategies;
-
-import com.google.auth.http.HttpTransportFactory;
-import com.google.auth.oauth2.GoogleCredentials;
-import org.apache.nifi.components.PropertyDescriptor;
-import 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors;
-
-import java.io.IOException;
-import java.util.Map;
-
-
-/**
- * Supports GCP Application Default Credentials.  Compared to 
ImplicitApplicationDefaultCredentialsStrategy, this
- * strategy is designed to be visible to the user, and depends on an 
affirmative selection from the user.
- *
- * @see <a 
href="https://developers.google.com/identity/protocols/application-default-credentials";>
- *     Application Default Credentials</a>
- */
-public class ExplicitApplicationDefaultCredentialsStrategy extends 
AbstractBooleanCredentialsStrategy {
-
-    public ExplicitApplicationDefaultCredentialsStrategy() {
-        super("Application Default Credentials", 
CredentialPropertyDescriptors.USE_APPLICATION_DEFAULT_CREDENTIALS);
-    }
-
-    @Override
-    public GoogleCredentials getGoogleCredentials(Map<PropertyDescriptor, 
String> properties, HttpTransportFactory transportFactory) throws IOException {
-        return GoogleCredentials.getApplicationDefault(transportFactory);
-    }
-
-}
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonFileServiceAccountCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonFileServiceAccountCredentialsStrategy.java
index 924a2ae22b..b05d1ea6fa 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonFileServiceAccountCredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonFileServiceAccountCredentialsStrategy.java
@@ -36,9 +36,7 @@ import java.util.Map;
 public class JsonFileServiceAccountCredentialsStrategy extends 
AbstractServiceAccountCredentialsStrategy {
 
     public JsonFileServiceAccountCredentialsStrategy() {
-        super("Service Account Credentials (Json File)", new 
PropertyDescriptor[] {
-                CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE
-        });
+        super("Service Account Credentials (Json File)");
     }
 
     @Override
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonStringServiceAccountCredentialsStrategy.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonStringServiceAccountCredentialsStrategy.java
index 3a40a7878f..f334d27298 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonStringServiceAccountCredentialsStrategy.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/factory/strategies/JsonStringServiceAccountCredentialsStrategy.java
@@ -33,9 +33,7 @@ import java.util.Map;
 public class JsonStringServiceAccountCredentialsStrategy extends 
AbstractServiceAccountCredentialsStrategy {
 
     public JsonStringServiceAccountCredentialsStrategy() {
-        super("Service Account Credentials (Json String)", new 
PropertyDescriptor[] {
-                CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON
-        });
+        super("Service Account Credentials (Json String)");
     }
 
     @Override
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerService.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerService.java
index fdaf38fd5c..5a2227112a 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerService.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/main/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerService.java
@@ -37,22 +37,26 @@ import org.apache.nifi.logging.ComponentLog;
 import org.apache.nifi.migration.PropertyConfiguration;
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processors.gcp.ProxyAwareTransportFactory;
+import 
org.apache.nifi.processors.gcp.credentials.factory.AuthenticationStrategy;
 import org.apache.nifi.processors.gcp.credentials.factory.CredentialsFactory;
 import org.apache.nifi.proxy.ProxyConfiguration;
 import org.apache.nifi.reporting.InitializationException;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
+import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.DELEGATION_STRATEGY;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.DELEGATION_USER;
+import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS;
+import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE;
-import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.USE_APPLICATION_DEFAULT_CREDENTIALS;
-import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.USE_COMPUTE_ENGINE_CREDENTIALS;
 
 /**
  * Implementation of GCPCredentialsService interface
@@ -76,8 +80,7 @@ import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPrope
 public class GCPCredentialsControllerService extends AbstractControllerService 
implements GCPCredentialsService, VerifiableControllerService {
 
     private static final List<PropertyDescriptor> PROPERTY_DESCRIPTORS = 
List.of(
-            USE_APPLICATION_DEFAULT_CREDENTIALS,
-            USE_COMPUTE_ENGINE_CREDENTIALS,
+            AUTHENTICATION_STRATEGY,
             SERVICE_ACCOUNT_JSON_FILE,
             SERVICE_ACCOUNT_JSON,
             
ProxyConfiguration.createProxyConfigPropertyDescriptor(ProxyAwareTransportFactory.PROXY_SPECS),
@@ -100,7 +103,7 @@ public class GCPCredentialsControllerService extends 
AbstractControllerService i
 
     @Override
     protected Collection<ValidationResult> customValidate(final 
ValidationContext validationContext) {
-        final Collection<ValidationResult> results = 
credentialsProviderFactory.validate(validationContext);
+        final Collection<ValidationResult> results = new ArrayList<>();
         ProxyConfiguration.validateProxySpec(validationContext, results, 
ProxyAwareTransportFactory.PROXY_SPECS);
         return results;
     }
@@ -136,10 +139,46 @@ public class GCPCredentialsControllerService extends 
AbstractControllerService i
 
     @Override
     public void migrateProperties(PropertyConfiguration config) {
-        config.renameProperty("application-default-credentials", 
USE_APPLICATION_DEFAULT_CREDENTIALS.getName());
-        config.renameProperty("compute-engine-credentials", 
USE_COMPUTE_ENGINE_CREDENTIALS.getName());
+        config.renameProperty("application-default-credentials", 
LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS.getName());
+        config.renameProperty("compute-engine-credentials", 
LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS.getName());
         config.renameProperty("service-account-json-file", 
SERVICE_ACCOUNT_JSON_FILE.getName());
         config.renameProperty("service-account-json", 
SERVICE_ACCOUNT_JSON.getName());
+
+        final boolean legacyFlagsPresent = 
config.hasProperty(LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS)
+                || config.hasProperty(LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS);
+        final Optional<String> authenticationStrategyValue = 
config.getRawPropertyValue(AUTHENTICATION_STRATEGY)
+                .map(String::trim)
+                .filter(value -> !value.isEmpty());
+        final boolean authenticationStrategyMissing = 
authenticationStrategyValue.isEmpty();
+
+        if (authenticationStrategyMissing || legacyFlagsPresent) {
+            final AuthenticationStrategy authenticationStrategy = 
determineAuthenticationStrategy(config);
+            if (authenticationStrategy != null) {
+                config.setProperty(AUTHENTICATION_STRATEGY, 
authenticationStrategy.getValue());
+            }
+        }
+
+        
config.removeProperty(LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS.getName());
+        config.removeProperty(LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS.getName());
+    }
+
+    private AuthenticationStrategy determineAuthenticationStrategy(final 
PropertyConfiguration config) {
+        if (isTrue(config, LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS)) {
+            return AuthenticationStrategy.APPLICATION_DEFAULT;
+        } else if (config.isPropertySet(SERVICE_ACCOUNT_JSON_FILE)) {
+            return AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE;
+        } else if (config.isPropertySet(SERVICE_ACCOUNT_JSON)) {
+            return AuthenticationStrategy.SERVICE_ACCOUNT_JSON;
+        } else if (isTrue(config, LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS)) {
+            return AuthenticationStrategy.COMPUTE_ENGINE;
+        }
+        return AuthenticationStrategy.APPLICATION_DEFAULT;
+    }
+
+    private boolean isTrue(final PropertyConfiguration config, final 
PropertyDescriptor property) {
+        return config.getRawPropertyValue(property)
+                .map(value -> "true".equalsIgnoreCase(value.trim()))
+                .orElse(false);
     }
 
     private GoogleCredentials getGoogleCredentials(final ConfigurationContext 
context) throws IOException {
@@ -152,4 +191,4 @@ public class GCPCredentialsControllerService extends 
AbstractControllerService i
     public String toString() {
         return "GCPCredentialsControllerService[id=" + getIdentifier() + "]";
     }
-}
\ No newline at end of file
+}
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/bigquery/PutBigQueryIT.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/bigquery/PutBigQueryIT.java
index c0c0f6354f..f503a3b732 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/bigquery/PutBigQueryIT.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/bigquery/PutBigQueryIT.java
@@ -67,6 +67,8 @@ import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
+import 
org.apache.nifi.processors.gcp.credentials.factory.AuthenticationStrategy;
+
 import static 
org.apache.nifi.processors.gcp.bigquery.AbstractBigQueryProcessor.DATASET;
 import static 
org.apache.nifi.processors.gcp.bigquery.AbstractBigQueryProcessor.TABLE_NAME;
 import static 
org.apache.nifi.processors.gcp.bigquery.PutBigQuery.RECORD_READER;
@@ -96,6 +98,7 @@ public class PutBigQueryIT {
     @BeforeAll
     public static void beforeClass() throws IOException {
         final Map<PropertyDescriptor, String> propertiesMap = new HashMap<>();
+        
propertiesMap.put(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         
propertiesMap.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE, 
SERVICE_ACCOUNT_JSON);
         Credentials credentials = 
credentialsProviderFactory.getGoogleCredentials(propertiesMap, new 
ProxyAwareTransportFactory(null));
 
@@ -120,6 +123,7 @@ public class PutBigQueryIT {
         final GCPCredentialsControllerService credentialsControllerService = 
new GCPCredentialsControllerService();
 
         final Map<String, String> propertiesMap = new HashMap<>();
+        
propertiesMap.put(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName(),
 AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         
propertiesMap.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE.getName(),
 SERVICE_ACCOUNT_JSON);
 
         runner.addControllerService(CONTROLLER_SERVICE, 
credentialsControllerService, propertiesMap);
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactoryTest.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactoryTest.java
index d2107d1b12..ac1fcf7597 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactoryTest.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/CredentialsFactoryTest.java
@@ -38,7 +38,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
- * Tests of the validation and credentials provider capabilities of 
CredentialsFactory.
+ * Tests of the credentials provider capabilities of CredentialsFactory.
  */
 public class CredentialsFactoryTest {
 
@@ -68,7 +68,7 @@ public class CredentialsFactoryTest {
     @Test
     public void testExplicitApplicationDefaultCredentials() throws Exception {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsFactoryProcessor.class);
-        
runner.setProperty(CredentialPropertyDescriptors.USE_APPLICATION_DEFAULT_CREDENTIALS,
 "true");
+        
runner.setProperty(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.APPLICATION_DEFAULT.getValue());
         runner.assertValid();
 
         Map<PropertyDescriptor, String> properties = 
runner.getProcessContext().getProperties();
@@ -79,16 +79,24 @@ public class CredentialsFactoryTest {
     }
 
     @Test
-    public void testExplicitApplicationDefaultCredentialsExclusive() throws 
Exception {
+    public void 
testExplicitApplicationDefaultCredentialsIgnoresServiceAccountFile() throws 
Exception {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsFactoryProcessor.class);
-        
runner.setProperty(CredentialPropertyDescriptors.USE_APPLICATION_DEFAULT_CREDENTIALS,
 "true");
-        
runner.setProperty(CredentialPropertyDescriptors.USE_COMPUTE_ENGINE_CREDENTIALS,
 "true");
-        runner.assertNotValid();
+        
runner.setProperty(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.APPLICATION_DEFAULT.getValue());
+        
runner.setProperty(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE,
+                "src/test/resources/mock-gcp-service-account.json");
+        runner.assertValid();
+
+        final Map<PropertyDescriptor, String> properties = 
runner.getProcessContext().getProperties();
+        final CredentialsFactory factory = new CredentialsFactory();
+        final GoogleCredentials credentials = 
factory.getGoogleCredentials(properties, TRANSPORT_FACTORY);
+
+        assertNotNull(credentials);
     }
 
     @Test
     public void testJsonFileCredentials() throws Exception {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsFactoryProcessor.class);
+        
runner.setProperty(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         
runner.setProperty(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE,
                 "src/test/resources/mock-gcp-service-account.json");
         runner.assertValid();
@@ -106,6 +114,7 @@ public class CredentialsFactoryTest {
     @Test
     public void testBadJsonFileCredentials() throws Exception {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsFactoryProcessor.class);
+        
runner.setProperty(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         
runner.setProperty(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE,
                 "src/test/resources/bad-mock-gcp-service-account.json");
         runner.assertNotValid();
@@ -117,6 +126,7 @@ public class CredentialsFactoryTest {
                 
Files.readAllBytes(Paths.get("src/test/resources/mock-gcp-service-account.json"))
         );
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsFactoryProcessor.class);
+        
runner.setProperty(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON.getValue());
         runner.setProperty(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON,
                 jsonRead);
         runner.assertValid();
@@ -133,7 +143,7 @@ public class CredentialsFactoryTest {
     @Test
     public void testComputeEngineCredentials() throws Exception {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsFactoryProcessor.class);
-        
runner.setProperty(CredentialPropertyDescriptors.USE_COMPUTE_ENGINE_CREDENTIALS,
 "true");
+        
runner.setProperty(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.COMPUTE_ENGINE.getValue());
         runner.assertValid();
 
         Map<PropertyDescriptor, String> properties = 
runner.getProcessContext().getProperties();
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/MockCredentialsFactoryProcessor.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/MockCredentialsFactoryProcessor.java
index 84614e961e..b57863b917 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/MockCredentialsFactoryProcessor.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/factory/MockCredentialsFactoryProcessor.java
@@ -20,31 +20,26 @@ import com.google.auth.oauth2.GoogleCredentials;
 import com.google.cloud.Service;
 import com.google.cloud.ServiceOptions;
 import org.apache.nifi.components.PropertyDescriptor;
-import org.apache.nifi.components.ValidationContext;
-import org.apache.nifi.components.ValidationResult;
 import org.apache.nifi.processor.ProcessContext;
 import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.exception.ProcessException;
 import org.apache.nifi.processors.gcp.AbstractGCPProcessor;
 
 import java.util.Arrays;
-import java.util.Collection;
 import java.util.List;
 
+import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE;
-import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.USE_APPLICATION_DEFAULT_CREDENTIALS;
-import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.USE_COMPUTE_ENGINE_CREDENTIALS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 
 public class MockCredentialsFactoryProcessor extends AbstractGCPProcessor {
     public final List<PropertyDescriptor> properties = Arrays.asList(
-            USE_APPLICATION_DEFAULT_CREDENTIALS,
+            AUTHENTICATION_STRATEGY,
             SERVICE_ACCOUNT_JSON,
-            SERVICE_ACCOUNT_JSON_FILE,
-            USE_COMPUTE_ENGINE_CREDENTIALS
+            SERVICE_ACCOUNT_JSON_FILE
     );
 
     @Override
@@ -65,10 +60,4 @@ public class MockCredentialsFactoryProcessor extends 
AbstractGCPProcessor {
     public void onTrigger(ProcessContext context, ProcessSession session) 
throws ProcessException {
 
     }
-
-    @Override
-    protected Collection<ValidationResult> customValidate(ValidationContext 
validationContext) {
-        CredentialsFactory factory = new CredentialsFactory();
-        return factory.validate(validationContext);
-    }
 }
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerServiceMigrationTest.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerServiceMigrationTest.java
new file mode 100644
index 0000000000..6a71c1995e
--- /dev/null
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsControllerServiceMigrationTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.processors.gcp.credentials.service;
+
+import 
org.apache.nifi.processors.gcp.credentials.factory.AuthenticationStrategy;
+import 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors;
+import org.apache.nifi.util.MockPropertyConfiguration;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+class GCPCredentialsControllerServiceMigrationTest {
+
+    private static final String SERVICE_ACCOUNT_JSON_PLACEHOLDER = 
"{\"mock\":\"json\"}";
+    private final GCPCredentialsControllerService service = new 
GCPCredentialsControllerService();
+
+    @Test
+    void testMigratesApplicationDefaultFlag() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS.getName(),
 "true");
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        assertEquals(AuthenticationStrategy.APPLICATION_DEFAULT.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+        
assertFalse(configuration.getRawProperties().containsKey(CredentialPropertyDescriptors.LEGACY_USE_APPLICATION_DEFAULT_CREDENTIALS.getName()));
+    }
+
+    @Test
+    void testMigratesServiceAccountFile() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE.getName(),
 "/tmp/account.json");
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        
assertEquals(AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+    }
+
+    @Test
+    void testMigratesServiceAccountJson() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON.getName(), 
SERVICE_ACCOUNT_JSON_PLACEHOLDER);
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        assertEquals(AuthenticationStrategy.SERVICE_ACCOUNT_JSON.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+    }
+
+    @Test
+    void testMigratesComputeEngineFlag() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS.getName(),
 "true");
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        assertEquals(AuthenticationStrategy.COMPUTE_ENGINE.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+        
assertFalse(configuration.getRawProperties().containsKey(CredentialPropertyDescriptors.LEGACY_USE_COMPUTE_ENGINE_CREDENTIALS.getName()));
+    }
+
+    @Test
+    void testDoesNotOverrideExistingAuthenticationStrategy() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName(), 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON.getValue());
+        
properties.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE.getName(),
 "/tmp/account.json");
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        assertEquals(AuthenticationStrategy.SERVICE_ACCOUNT_JSON.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+    }
+
+    @Test
+    void testSetsDefaultAuthenticationStrategyWhenUnset() {
+        final Map<String, String> properties = new HashMap<>();
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+
+        service.migrateProperties(configuration);
+
+        assertEquals(AuthenticationStrategy.APPLICATION_DEFAULT.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+    }
+
+    @Test
+    void testLegacyPropertiesDeriveAuthenticationStrategyWhenMissing() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE.getName(),
 "/tmp/account.json");
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        
assertEquals(AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+    }
+
+    @Test
+    void testDoesNotOverrideExplicitStrategyWhenServiceAccountPropertiesSet() {
+        final Map<String, String> properties = new HashMap<>();
+        
properties.put(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName(), 
AuthenticationStrategy.APPLICATION_DEFAULT.getValue());
+        
properties.put(CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE.getName(),
 "/tmp/account.json");
+
+        final MockPropertyConfiguration configuration = new 
MockPropertyConfiguration(properties);
+        service.migrateProperties(configuration);
+
+        assertEquals(AuthenticationStrategy.APPLICATION_DEFAULT.getValue(),
+                
configuration.getRawProperties().get(CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY.getName()));
+    }
+}
diff --git 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsServiceTest.java
 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsServiceTest.java
index 95ed29fe8a..ab04129260 100644
--- 
a/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsServiceTest.java
+++ 
b/nifi-extension-bundles/nifi-gcp-bundle/nifi-gcp-processors/src/test/java/org/apache/nifi/processors/gcp/credentials/service/GCPCredentialsServiceTest.java
@@ -19,6 +19,7 @@ package org.apache.nifi.processors.gcp.credentials.service;
 import com.google.auth.oauth2.GoogleCredentials;
 import com.google.auth.oauth2.ServiceAccountCredentials;
 import org.apache.nifi.gcp.credentials.service.GCPCredentialsService;
+import 
org.apache.nifi.processors.gcp.credentials.factory.AuthenticationStrategy;
 import org.apache.nifi.util.TestRunner;
 import org.apache.nifi.util.TestRunners;
 import org.junit.jupiter.api.Test;
@@ -26,10 +27,9 @@ import org.junit.jupiter.api.Test;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 
+import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.AUTHENTICATION_STRATEGY;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON;
 import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.SERVICE_ACCOUNT_JSON_FILE;
-import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.USE_APPLICATION_DEFAULT_CREDENTIALS;
-import static 
org.apache.nifi.processors.gcp.credentials.factory.CredentialPropertyDescriptors.USE_COMPUTE_ENGINE_CREDENTIALS;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 
@@ -66,7 +66,7 @@ public class GCPCredentialsServiceTest {
         final GCPCredentialsControllerService serviceImpl = new 
GCPCredentialsControllerService();
         runner.addControllerService("gcpCredentialsProvider", serviceImpl);
 
-        runner.setProperty(serviceImpl, USE_APPLICATION_DEFAULT_CREDENTIALS, 
"true");
+        runner.setProperty(serviceImpl, AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.APPLICATION_DEFAULT.getValue());
         runner.enableControllerService(serviceImpl);
 
         runner.assertValid(serviceImpl);
@@ -85,6 +85,7 @@ public class GCPCredentialsServiceTest {
         final GCPCredentialsControllerService serviceImpl = new 
GCPCredentialsControllerService();
         runner.addControllerService("gcpCredentialsProvider", serviceImpl);
 
+        runner.setProperty(serviceImpl, AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         runner.setProperty(serviceImpl, SERVICE_ACCOUNT_JSON_FILE,
                 "src/test/resources/mock-gcp-service-account.json");
         runner.enableControllerService(serviceImpl);
@@ -107,23 +108,25 @@ public class GCPCredentialsServiceTest {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsServiceProcessor.class);
         final GCPCredentialsControllerService serviceImpl = new 
GCPCredentialsControllerService();
         runner.addControllerService("gcpCredentialsProvider", serviceImpl);
+        runner.setProperty(serviceImpl, AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         runner.setProperty(serviceImpl, SERVICE_ACCOUNT_JSON_FILE,
                 "src/test/resources/bad-mock-gcp-service-account.json");
         runner.assertNotValid(serviceImpl);
     }
 
     @Test
-    public void testMultipleCredentialSources() throws Exception {
+    public void testMultipleCredentialSourcesRemainValid() throws Exception {
         final TestRunner runner = 
TestRunners.newTestRunner(MockCredentialsServiceProcessor.class);
         final GCPCredentialsControllerService serviceImpl = new 
GCPCredentialsControllerService();
 
         runner.addControllerService("gcpCredentialsProvider", serviceImpl);
+        runner.setProperty(serviceImpl, AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON_FILE.getValue());
         runner.setProperty(serviceImpl, SERVICE_ACCOUNT_JSON_FILE,
                 "src/test/resources/mock-gcp-service-account.json");
-        runner.setProperty(serviceImpl, USE_APPLICATION_DEFAULT_CREDENTIALS, 
"true");
-        runner.setProperty(serviceImpl, USE_COMPUTE_ENGINE_CREDENTIALS, 
"true");
+        runner.setProperty(serviceImpl, SERVICE_ACCOUNT_JSON,
+                "{\"mock\":\"json\"}");
 
-        runner.assertNotValid(serviceImpl);
+        runner.assertValid(serviceImpl);
     }
 
     @Test
@@ -136,6 +139,7 @@ public class GCPCredentialsServiceTest {
         final GCPCredentialsControllerService serviceImpl = new 
GCPCredentialsControllerService();
         runner.addControllerService("gcpCredentialsProvider", serviceImpl);
 
+        runner.setProperty(serviceImpl, AUTHENTICATION_STRATEGY, 
AuthenticationStrategy.SERVICE_ACCOUNT_JSON.getValue());
         runner.setProperty(serviceImpl, SERVICE_ACCOUNT_JSON,
                 jsonRead);
         runner.enableControllerService(serviceImpl);

Reply via email to