diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java index bb22c576a3..e6ca356200 100644 --- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java +++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/processors/azure/AbstractAzureDataLakeStorageProcessor.java @@ -25,6 +25,8 @@ import java.util.Set; import com.azure.core.credential.AccessToken; import com.azure.core.credential.TokenCredential; +import com.azure.identity.ClientSecretCredential; +import com.azure.identity.ClientSecretCredentialBuilder; import com.azure.identity.ManagedIdentityCredential; import com.azure.identity.ManagedIdentityCredentialBuilder; import com.azure.storage.common.StorageSharedKeyCredential; @@ -53,12 +55,12 @@ import static org.apache.nifi.processors.azure.storage.utils.ADLSAttributes.ATTR public abstract class AbstractAzureDataLakeStorageProcessor extends AbstractProcessor { public static final PropertyDescriptor ADLS_CREDENTIALS_SERVICE = new PropertyDescriptor.Builder() - .name("adls-credentials-service") - .displayName("ADLS Credentials") - .description("Controller Service used to obtain Azure Credentials.") - .identifiesControllerService(ADLSCredentialsService.class) - .required(true) - .build(); + .name("adls-credentials-service") + .displayName("ADLS Credentials") + .description("Controller Service used to obtain Azure Credentials.") + .identifiesControllerService(ADLSCredentialsService.class) + .required(true) + .build(); public static final PropertyDescriptor FILESYSTEM = new PropertyDescriptor.Builder() .name("filesystem-name").displayName("Filesystem Name") @@ -120,7 +122,7 @@ public abstract class AbstractAzureDataLakeStorageProcessor extends AbstractProc final ADLSCredentialsService credentialsService = context.getProperty(ADLS_CREDENTIALS_SERVICE).asControllerService(ADLSCredentialsService.class); - ADLSCredentialsDetails credentialsDetails = credentialsService.getCredentialsDetails(attributes); + final ADLSCredentialsDetails credentialsDetails = credentialsService.getCredentialsDetails(attributes); final String accountName = credentialsDetails.getAccountName(); final String accountKey = credentialsDetails.getAccountKey(); @@ -128,9 +130,13 @@ public abstract class AbstractAzureDataLakeStorageProcessor extends AbstractProc final AccessToken accessToken = credentialsDetails.getAccessToken(); final String endpointSuffix = credentialsDetails.getEndpointSuffix(); final boolean useManagedIdentity = credentialsDetails.getUseManagedIdentity(); + final String servicePrincipalTenantId = credentialsDetails.getServicePrincipalTenantId(); + final String servicePrincipalClientId = credentialsDetails.getServicePrincipalClientId(); + final String servicePrincipalClientSecret = credentialsDetails.getServicePrincipalClientSecret(); final String endpoint = String.format("https://%s.%s", accountName,endpointSuffix); - DataLakeServiceClient storageClient; + + final DataLakeServiceClient storageClient; if (StringUtils.isNotBlank(accountKey)) { final StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey); @@ -140,17 +146,28 @@ public abstract class AbstractAzureDataLakeStorageProcessor extends AbstractProc storageClient = new DataLakeServiceClientBuilder().endpoint(endpoint).sasToken(sasToken) .buildClient(); } else if (accessToken != null) { - TokenCredential credential = tokenRequestContext -> Mono.just(accessToken); + final TokenCredential credential = tokenRequestContext -> Mono.just(accessToken); storageClient = new DataLakeServiceClientBuilder().endpoint(endpoint).credential(credential) - .buildClient(); - } else if(useManagedIdentity){ - final ManagedIdentityCredential misCrendential = new ManagedIdentityCredentialBuilder() - .build(); - storageClient = new DataLakeServiceClientBuilder() - .endpoint(endpoint) - .credential(misCrendential) - .buildClient(); + .buildClient(); + } else if (useManagedIdentity) { + final ManagedIdentityCredential misCredential = new ManagedIdentityCredentialBuilder() + .build(); + storageClient = new DataLakeServiceClientBuilder() + .endpoint(endpoint) + .credential(misCredential) + .buildClient(); + } else if (StringUtils.isNoneBlank(servicePrincipalTenantId, servicePrincipalClientId, servicePrincipalClientSecret)) { + final ClientSecretCredential credential = new ClientSecretCredentialBuilder() + .tenantId(servicePrincipalTenantId) + .clientId(servicePrincipalClientId) + .clientSecret(servicePrincipalClientSecret) + .build(); + + storageClient = new DataLakeServiceClientBuilder() + .endpoint(endpoint) + .credential(credential) + .buildClient(); } else { throw new IllegalArgumentException("No valid credentials were provided"); } diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsControllerService.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsControllerService.java index ec86f9ce3f..8ecb554487 100644 --- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsControllerService.java +++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsControllerService.java @@ -26,6 +26,7 @@ import org.apache.nifi.components.ValidationContext; import org.apache.nifi.components.ValidationResult; import org.apache.nifi.controller.AbstractControllerService; import org.apache.nifi.controller.ConfigurationContext; +import org.apache.nifi.expression.ExpressionLanguageScope; import org.apache.nifi.processor.util.StandardValidators; import org.apache.nifi.processors.azure.storage.utils.AzureStorageUtils; @@ -35,7 +36,6 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.StringJoiner; import java.util.function.BiConsumer; import java.util.function.Function; @@ -49,37 +49,81 @@ import java.util.function.Function; public class ADLSCredentialsControllerService extends AbstractControllerService implements ADLSCredentialsService { public static final PropertyDescriptor ACCOUNT_NAME = new PropertyDescriptor.Builder() - .fromPropertyDescriptor(AzureStorageUtils.ACCOUNT_NAME) - .description(AzureStorageUtils.ACCOUNT_NAME_BASE_DESCRIPTION) - .required(true) - .build(); + .fromPropertyDescriptor(AzureStorageUtils.ACCOUNT_NAME) + .description(AzureStorageUtils.ACCOUNT_NAME_BASE_DESCRIPTION) + .required(true) + .expressionLanguageSupported(ExpressionLanguageScope.NONE) + .build(); public static final PropertyDescriptor ENDPOINT_SUFFIX = new PropertyDescriptor.Builder() - .fromPropertyDescriptor(AzureStorageUtils.ENDPOINT_SUFFIX) - .displayName("Endpoint Suffix") - .description( - "Storage accounts in public Azure always use a common FQDN suffix. " + - "Override this endpoint suffix with a different suffix in certain circumstances (like Azure Stack or non-public Azure regions).") - .required(true) - .defaultValue("dfs.core.windows.net") - .build(); + .fromPropertyDescriptor(AzureStorageUtils.ENDPOINT_SUFFIX) + .displayName("Endpoint Suffix") + .description("Storage accounts in public Azure always use a common FQDN suffix. " + + "Override this endpoint suffix with a different suffix in certain circumstances (like Azure Stack or non-public Azure regions).") + .required(true) + .defaultValue("dfs.core.windows.net") + .expressionLanguageSupported(ExpressionLanguageScope.VARIABLE_REGISTRY) + .build(); + + public static final PropertyDescriptor ACCOUNT_KEY = new PropertyDescriptor.Builder() + .fromPropertyDescriptor(AzureStorageUtils.ACCOUNT_KEY) + .expressionLanguageSupported(ExpressionLanguageScope.NONE) + .build(); + + public static final PropertyDescriptor SAS_TOKEN = new PropertyDescriptor.Builder() + .fromPropertyDescriptor(AzureStorageUtils.PROP_SAS_TOKEN) + .expressionLanguageSupported(ExpressionLanguageScope.NONE) + .build(); public static final PropertyDescriptor USE_MANAGED_IDENTITY = new PropertyDescriptor.Builder() - .name("storage-use-managed-identity") - .displayName("Use Azure Managed Identity") - .description("Choose whether or not to use the managed identity of Azure VM/VMSS ") - .required(false) - .defaultValue("false") - .allowableValues("true", "false") - .addValidator(StandardValidators.BOOLEAN_VALIDATOR) - .build(); + .name("storage-use-managed-identity") + .displayName("Use Azure Managed Identity") + .description("Choose whether or not to use the managed identity of Azure VM/VMSS ") + .required(false) + .defaultValue("false") + .allowableValues("true", "false") + .addValidator(StandardValidators.BOOLEAN_VALIDATOR) + .build(); + + public static final PropertyDescriptor SERVICE_PRINCIPAL_TENANT_ID = new PropertyDescriptor.Builder() + .name("service-principal-tenant-id") + .displayName("Service Principal Tenant ID") + .description("Tenant ID of the Azure Active Directory hosting the Service Principal. The property is required when Service Principal authentication is used.") + .sensitive(true) + .required(false) + .addValidator(StandardValidators.NON_BLANK_VALIDATOR) + .expressionLanguageSupported(ExpressionLanguageScope.NONE) + .build(); + + public static final PropertyDescriptor SERVICE_PRINCIPAL_CLIENT_ID = new PropertyDescriptor.Builder() + .name("service-principal-client-id") + .displayName("Service Principal Client ID") + .description("Client ID (or Application ID) of the Client/Application having the Service Principal. The property is required when Service Principal authentication is used.") + .sensitive(true) + .required(false) + .addValidator(StandardValidators.NON_BLANK_VALIDATOR) + .expressionLanguageSupported(ExpressionLanguageScope.NONE) + .build(); + + public static final PropertyDescriptor SERVICE_PRINCIPAL_CLIENT_SECRET = new PropertyDescriptor.Builder() + .name("service-principal-client-secret") + .displayName("Service Principal Client Secret") + .description("Password of the Client/Application. The property is required when Service Principal authentication is used.") + .sensitive(true) + .required(false) + .addValidator(StandardValidators.NON_BLANK_VALIDATOR) + .expressionLanguageSupported(ExpressionLanguageScope.NONE) + .build(); private static final List PROPERTIES = Collections.unmodifiableList(Arrays.asList( - ACCOUNT_NAME, - ENDPOINT_SUFFIX, - AzureStorageUtils.ACCOUNT_KEY, - AzureStorageUtils.PROP_SAS_TOKEN, - USE_MANAGED_IDENTITY + ACCOUNT_NAME, + ENDPOINT_SUFFIX, + ACCOUNT_KEY, + SAS_TOKEN, + USE_MANAGED_IDENTITY, + SERVICE_PRINCIPAL_TENANT_ID, + SERVICE_PRINCIPAL_CLIENT_ID, + SERVICE_PRINCIPAL_CLIENT_SECRET )); private ConfigurationContext context; @@ -93,20 +137,41 @@ public class ADLSCredentialsControllerService extends AbstractControllerService protected Collection customValidate(ValidationContext validationContext) { final List results = new ArrayList<>(); - boolean accountKeySet = StringUtils.isNotBlank(validationContext.getProperty(AzureStorageUtils.ACCOUNT_KEY).getValue()); - boolean sasTokenSet = StringUtils.isNotBlank(validationContext.getProperty(AzureStorageUtils.PROP_SAS_TOKEN).getValue()); + boolean accountKeySet = StringUtils.isNotBlank(validationContext.getProperty(ACCOUNT_KEY).getValue()); + boolean sasTokenSet = StringUtils.isNotBlank(validationContext.getProperty(SAS_TOKEN).getValue()); boolean useManagedIdentitySet = validationContext.getProperty(USE_MANAGED_IDENTITY).asBoolean(); - if (!onlyOneSet(accountKeySet, sasTokenSet, useManagedIdentitySet)) { - StringJoiner options = new StringJoiner(", ") - .add(AzureStorageUtils.ACCOUNT_KEY.getDisplayName()) - .add(AzureStorageUtils.PROP_SAS_TOKEN.getDisplayName()) - .add(USE_MANAGED_IDENTITY.getDisplayName()); + boolean servicePrincipalTenantIdSet = StringUtils.isNotBlank(validationContext.getProperty(SERVICE_PRINCIPAL_TENANT_ID).getValue()); + boolean servicePrincipalClientIdSet = StringUtils.isNotBlank(validationContext.getProperty(SERVICE_PRINCIPAL_CLIENT_ID).getValue()); + boolean servicePrincipalClientSecretSet = StringUtils.isNotBlank(validationContext.getProperty(SERVICE_PRINCIPAL_CLIENT_SECRET).getValue()); + boolean servicePrincipalSet = servicePrincipalTenantIdSet || servicePrincipalClientIdSet || servicePrincipalClientSecretSet; + + if (!onlyOneSet(accountKeySet, sasTokenSet, useManagedIdentitySet, servicePrincipalSet)) { results.add(new ValidationResult.Builder().subject(this.getClass().getSimpleName()) .valid(false) - .explanation("one and only one of [" + options + "] should be set") + .explanation("one and only one authentication method of [Account Key, SAS Token, Managed Identity, Service Principal] should be used") .build()); + } else if (servicePrincipalSet) { + String template = "'%s' must be set when Service Principal authentication is being configured"; + if (!servicePrincipalTenantIdSet) { + results.add(new ValidationResult.Builder().subject(this.getClass().getSimpleName()) + .valid(false) + .explanation(String.format(template, SERVICE_PRINCIPAL_TENANT_ID.getDisplayName())) + .build()); + } + if (!servicePrincipalClientIdSet) { + results.add(new ValidationResult.Builder().subject(this.getClass().getSimpleName()) + .valid(false) + .explanation(String.format(template, SERVICE_PRINCIPAL_CLIENT_ID.getDisplayName())) + .build()); + } + if (!servicePrincipalClientSecretSet) { + results.add(new ValidationResult.Builder().subject(this.getClass().getSimpleName()) + .valid(false) + .explanation(String.format(template, SERVICE_PRINCIPAL_CLIENT_SECRET.getDisplayName())) + .build()); + } } return results; @@ -129,23 +194,29 @@ public class ADLSCredentialsControllerService extends AbstractControllerService public ADLSCredentialsDetails getCredentialsDetails(Map attributes) { ADLSCredentialsDetails.Builder credentialsBuilder = ADLSCredentialsDetails.Builder.newBuilder(); - setValue(credentialsBuilder, ACCOUNT_NAME, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setAccountName); - setValue(credentialsBuilder, AzureStorageUtils.ACCOUNT_KEY, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setAccountKey); - setValue(credentialsBuilder, AzureStorageUtils.PROP_SAS_TOKEN, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setSasToken); - setValue(credentialsBuilder, ENDPOINT_SUFFIX, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setEndpointSuffix); - setValue(credentialsBuilder, USE_MANAGED_IDENTITY, PropertyValue::asBoolean, ADLSCredentialsDetails.Builder::setUseManagedIdentity); + setValue(credentialsBuilder, ACCOUNT_NAME, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setAccountName, attributes); + setValue(credentialsBuilder, ACCOUNT_KEY, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setAccountKey, attributes); + setValue(credentialsBuilder, SAS_TOKEN, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setSasToken, attributes); + setValue(credentialsBuilder, ENDPOINT_SUFFIX, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setEndpointSuffix, attributes); + setValue(credentialsBuilder, USE_MANAGED_IDENTITY, PropertyValue::asBoolean, ADLSCredentialsDetails.Builder::setUseManagedIdentity, attributes); + setValue(credentialsBuilder, SERVICE_PRINCIPAL_TENANT_ID, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setServicePrincipalTenantId, attributes); + setValue(credentialsBuilder, SERVICE_PRINCIPAL_CLIENT_ID, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setServicePrincipalClientId, attributes); + setValue(credentialsBuilder, SERVICE_PRINCIPAL_CLIENT_SECRET, PropertyValue::getValue, ADLSCredentialsDetails.Builder::setServicePrincipalClientSecret, attributes); return credentialsBuilder.build(); } private void setValue( - ADLSCredentialsDetails.Builder credentialsBuilder, - PropertyDescriptor propertyDescriptor, Function getPropertyValue, - BiConsumer setBuilderValue + ADLSCredentialsDetails.Builder credentialsBuilder, + PropertyDescriptor propertyDescriptor, Function getPropertyValue, + BiConsumer setBuilderValue, Map attributes ) { PropertyValue property = context.getProperty(propertyDescriptor); if (property.isSet()) { + if (propertyDescriptor.isExpressionLanguageSupported()) { + property = property.evaluateAttributeExpressions(attributes); + } T value = getPropertyValue.apply(property); setBuilderValue.accept(credentialsBuilder, value); } diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/test/java/org/apache/nifi/services/azure/storage/TestADLSCredentialsControllerService.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/test/java/org/apache/nifi/services/azure/storage/TestADLSCredentialsControllerService.java index aa51c4c861..6837c9ebb5 100644 --- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/test/java/org/apache/nifi/services/azure/storage/TestADLSCredentialsControllerService.java +++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-processors/src/test/java/org/apache/nifi/services/azure/storage/TestADLSCredentialsControllerService.java @@ -16,7 +16,7 @@ */ package org.apache.nifi.services.azure.storage; -import org.apache.nifi.processors.azure.storage.utils.AzureStorageUtils; +import org.apache.nifi.components.PropertyDescriptor; import org.apache.nifi.reporting.InitializationException; import org.apache.nifi.util.NoOpProcessor; import org.apache.nifi.util.TestRunner; @@ -39,7 +39,10 @@ public class TestADLSCredentialsControllerService { private static final String ACCOUNT_NAME_VALUE = "AccountName"; private static final String ACCOUNT_KEY_VALUE = "AccountKey"; private static final String SAS_TOKEN_VALUE = "SasToken"; - public static final String END_POINT_SUFFIX_VALUE = "end.point.suffix"; + private static final String END_POINT_SUFFIX_VALUE = "end.point.suffix"; + private static final String SERVICE_PRINCIPAL_TENANT_ID_VALUE = "ServicePrincipalTenantID"; + private static final String SERVICE_PRINCIPAL_CLIENT_ID_VALUE = "ServicePrincipalClientID"; + private static final String SERVICE_PRINCIPAL_CLIENT_SECRET_VALUE = "ServicePrincipalClientSecret"; private TestRunner runner; private ADLSCredentialsControllerService credentialsService; @@ -85,6 +88,36 @@ public class TestADLSCredentialsControllerService { runner.assertNotValid(credentialsService); } + @Test + public void testNotValidBecauseBothAccountKeyAndServicePrincipalTenantIdSpecified() { + configureAccountName(); + + configureAccountKey(); + configureServicePrincipalTenantId(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothAccountKeyAndServicePrincipalClientIdSpecified() { + configureAccountName(); + + configureAccountKey(); + configureServicePrincipalClientId(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothAccountKeyAndServicePrincipalClientSecretSpecified() { + configureAccountName(); + + configureAccountKey(); + configureServicePrincipalClientSecret(); + + runner.assertNotValid(credentialsService); + } + @Test public void testNotValidBecauseBothSasTokenAndUseManagedIdentitySpecified() { configureAccountName(); @@ -96,12 +129,75 @@ public class TestADLSCredentialsControllerService { } @Test - public void testNotValidBecauseAllCredentialsSpecified() { + public void testNotValidBecauseBothSasTokenAndServicePrincipalTenantIdSpecified() { + configureAccountName(); + + configureSasToken(); + configureServicePrincipalTenantId(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothSasTokenAndServicePrincipalClientIdSpecified() { + configureAccountName(); + + configureSasToken(); + configureServicePrincipalClientId(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothSasTokenAndServicePrincipalClientSecretSpecified() { + configureAccountName(); + + configureSasToken(); + configureServicePrincipalClientSecret(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothUseManagedIdentityAndServicePrincipalTenantIdSpecified() { + configureAccountName(); + + configureUseManagedIdentity(); + configureServicePrincipalTenantId(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothUseManagedIdentityAndServicePrincipalClientIdSpecified() { + configureAccountName(); + + configureUseManagedIdentity(); + configureServicePrincipalClientId(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseBothUseManagedIdentityAndServicePrincipalClientSecretSpecified() { + configureAccountName(); + + configureUseManagedIdentity(); + configureServicePrincipalClientSecret(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseAllCredentialsSpecified() throws Exception { configureAccountName(); configureAccountKey(); configureSasToken(); configureUseManagedIdentity(); + configureServicePrincipalTenantId(); + configureServicePrincipalClientId(); + configureServicePrincipalClientSecret(); runner.assertNotValid(credentialsService); } @@ -147,6 +243,46 @@ public class TestADLSCredentialsControllerService { runner.assertValid(credentialsService); } + @Test + public void testValidWithAccountNameAndServicePrincipalWithClientSecret() { + configureAccountName(); + configureServicePrincipalTenantId(); + configureServicePrincipalClientId(); + configureServicePrincipalClientSecret(); + + runner.assertValid(credentialsService); + } + + @Test + public void testNotValidBecauseNoTenantIdSpecifiedForServicePrincipal() { + configureAccountName(); + + configureServicePrincipalClientId(); + configureServicePrincipalClientSecret(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseNoClientIdSpecifiedForServicePrincipal() { + configureAccountName(); + + configureServicePrincipalTenantId(); + configureServicePrincipalClientSecret(); + + runner.assertNotValid(credentialsService); + } + + @Test + public void testNotValidBecauseNoClientSecretSpecifiedForServicePrincipal() { + configureAccountName(); + + configureServicePrincipalTenantId(); + configureServicePrincipalClientId(); + + runner.assertNotValid(credentialsService); + } + @Test public void testGetCredentialsDetailsWithAccountKey() throws Exception { // GIVEN @@ -164,6 +300,9 @@ public class TestADLSCredentialsControllerService { assertNull(actual.getSasToken()); assertFalse(actual.getUseManagedIdentity()); assertNotNull(actual.getEndpointSuffix()); + assertNull(actual.getServicePrincipalTenantId()); + assertNull(actual.getServicePrincipalClientId()); + assertNull(actual.getServicePrincipalClientSecret()); } @Test @@ -183,6 +322,9 @@ public class TestADLSCredentialsControllerService { assertNull(actual.getAccountKey()); assertFalse(actual.getUseManagedIdentity()); assertNotNull(actual.getEndpointSuffix()); + assertNull(actual.getServicePrincipalTenantId()); + assertNull(actual.getServicePrincipalClientId()); + assertNull(actual.getServicePrincipalClientSecret()); } @Test @@ -202,6 +344,33 @@ public class TestADLSCredentialsControllerService { assertNull(actual.getAccountKey()); assertNull(actual.getSasToken()); assertNotNull(actual.getEndpointSuffix()); + assertNull(actual.getServicePrincipalTenantId()); + assertNull(actual.getServicePrincipalClientId()); + assertNull(actual.getServicePrincipalClientSecret()); + } + + @Test + public void testGetCredentialsDetailsWithServicePrincipalWithClientSecret() throws Exception { + // GIVEN + configureAccountName(); + configureServicePrincipalTenantId(); + configureServicePrincipalClientId(); + configureServicePrincipalClientSecret(); + + runner.enableControllerService(credentialsService); + + // WHEN + ADLSCredentialsDetails actual = credentialsService.getCredentialsDetails(new HashMap<>()); + + // THEN + assertEquals(ACCOUNT_NAME_VALUE, actual.getAccountName()); + assertNull(actual.getAccountKey()); + assertNull(actual.getSasToken()); + assertFalse(actual.getUseManagedIdentity()); + assertNotNull(actual.getEndpointSuffix()); + assertEquals(SERVICE_PRINCIPAL_TENANT_ID_VALUE, actual.getServicePrincipalTenantId()); + assertEquals(SERVICE_PRINCIPAL_CLIENT_ID_VALUE, actual.getServicePrincipalClientId()); + assertEquals(SERVICE_PRINCIPAL_CLIENT_SECRET_VALUE, actual.getServicePrincipalClientSecret()); } @Test @@ -220,16 +389,32 @@ public class TestADLSCredentialsControllerService { assertEquals(END_POINT_SUFFIX_VALUE, actual.getEndpointSuffix()); } + @Test + public void testGetCredentialsDetailsWithSetEndpointSuffixUsingEL() throws Exception { + // GIVEN + configureAccountName(); + configureAccountKey(); + configureEndpointSuffixUsingEL(); + + runner.enableControllerService(credentialsService); + + // WHEN + ADLSCredentialsDetails actual = credentialsService.getCredentialsDetails(new HashMap<>()); + + // THEN + assertEquals(END_POINT_SUFFIX_VALUE, actual.getEndpointSuffix()); + } + private void configureAccountName() { runner.setProperty(credentialsService, ADLSCredentialsControllerService.ACCOUNT_NAME, ACCOUNT_NAME_VALUE); } private void configureAccountKey() { - runner.setProperty(credentialsService, AzureStorageUtils.ACCOUNT_KEY, ACCOUNT_KEY_VALUE); + runner.setProperty(credentialsService, ADLSCredentialsControllerService.ACCOUNT_KEY, ACCOUNT_KEY_VALUE); } private void configureSasToken() { - runner.setProperty(credentialsService, AzureStorageUtils.PROP_SAS_TOKEN, SAS_TOKEN_VALUE); + runner.setProperty(credentialsService, ADLSCredentialsControllerService.SAS_TOKEN, SAS_TOKEN_VALUE); } private void configureUseManagedIdentity() { @@ -239,4 +424,26 @@ public class TestADLSCredentialsControllerService { private void configureEndpointSuffix() { runner.setProperty(credentialsService, ADLSCredentialsControllerService.ENDPOINT_SUFFIX, END_POINT_SUFFIX_VALUE); } + + private void configureEndpointSuffixUsingEL() { + String variableName = "endpoint.suffix"; + configurePropertyUsingEL(ADLSCredentialsControllerService.ENDPOINT_SUFFIX, variableName, END_POINT_SUFFIX_VALUE); + } + + private void configureServicePrincipalTenantId() { + runner.setProperty(credentialsService, ADLSCredentialsControllerService.SERVICE_PRINCIPAL_TENANT_ID, SERVICE_PRINCIPAL_TENANT_ID_VALUE); + } + + private void configureServicePrincipalClientId() { + runner.setProperty(credentialsService, ADLSCredentialsControllerService.SERVICE_PRINCIPAL_CLIENT_ID, SERVICE_PRINCIPAL_CLIENT_ID_VALUE); + } + + private void configureServicePrincipalClientSecret() { + runner.setProperty(credentialsService, ADLSCredentialsControllerService.SERVICE_PRINCIPAL_CLIENT_SECRET, SERVICE_PRINCIPAL_CLIENT_SECRET_VALUE); + } + + private void configurePropertyUsingEL(PropertyDescriptor propertyDescriptor, String variableName, String variableValue) { + runner.setProperty(credentialsService, propertyDescriptor, String.format("${%s}", variableName)); + runner.setVariable(variableName, variableValue); + } } diff --git a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-services-api/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsDetails.java b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-services-api/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsDetails.java index cd1111e5be..03143ed7e2 100644 --- a/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-services-api/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsDetails.java +++ b/nifi-nar-bundles/nifi-azure-bundle/nifi-azure-services-api/src/main/java/org/apache/nifi/services/azure/storage/ADLSCredentialsDetails.java @@ -29,13 +29,20 @@ public class ADLSCredentialsDetails { private final boolean useManagedIdentity; + private final String servicePrincipalTenantId; + private final String servicePrincipalClientId; + private final String servicePrincipalClientSecret; + public ADLSCredentialsDetails( - String accountName, - String accountKey, - String sasToken, - String endpointSuffix, - AccessToken accessToken, - boolean useManagedIdentity + String accountName, + String accountKey, + String sasToken, + String endpointSuffix, + AccessToken accessToken, + boolean useManagedIdentity, + String servicePrincipalTenantId, + String servicePrincipalClientId, + String servicePrincipalClientSecret ) { this.accountName = accountName; this.accountKey = accountKey; @@ -43,6 +50,9 @@ public class ADLSCredentialsDetails { this.endpointSuffix = endpointSuffix; this.accessToken = accessToken; this.useManagedIdentity = useManagedIdentity; + this.servicePrincipalTenantId = servicePrincipalTenantId; + this.servicePrincipalClientId = servicePrincipalClientId; + this.servicePrincipalClientSecret = servicePrincipalClientSecret; } public String getAccountName() { @@ -69,6 +79,18 @@ public class ADLSCredentialsDetails { return useManagedIdentity; } + public String getServicePrincipalTenantId() { + return servicePrincipalTenantId; + } + + public String getServicePrincipalClientId() { + return servicePrincipalClientId; + } + + public String getServicePrincipalClientSecret() { + return servicePrincipalClientSecret; + } + public static class Builder { private String accountName; private String accountKey; @@ -76,6 +98,9 @@ public class ADLSCredentialsDetails { private String endpointSuffix; private AccessToken accessToken; private boolean useManagedIdentity; + private String servicePrincipalTenantId; + private String servicePrincipalClientId; + private String servicePrincipalClientSecret; private Builder() {} @@ -113,8 +138,24 @@ public class ADLSCredentialsDetails { return this; } + public Builder setServicePrincipalTenantId(String servicePrincipalTenantId) { + this.servicePrincipalTenantId = servicePrincipalTenantId; + return this; + } + + public Builder setServicePrincipalClientId(String servicePrincipalClientId) { + this.servicePrincipalClientId = servicePrincipalClientId; + return this; + } + + public Builder setServicePrincipalClientSecret(String servicePrincipalClientSecret) { + this.servicePrincipalClientSecret = servicePrincipalClientSecret; + return this; + } + public ADLSCredentialsDetails build() { - return new ADLSCredentialsDetails(accountName, accountKey, sasToken, endpointSuffix, accessToken, useManagedIdentity); + return new ADLSCredentialsDetails(accountName, accountKey, sasToken, endpointSuffix, accessToken, useManagedIdentity, + servicePrincipalTenantId, servicePrincipalClientId, servicePrincipalClientSecret); } } }