This is an automated email from the ASF dual-hosted git repository. szaszm pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
commit 26409cfd52c762bb6c5242a594a849edac1b19e7 Author: Gabor Gyimesi <[email protected]> AuthorDate: Tue Sep 30 16:26:51 2025 +0200 MINIFICPP-2614 Add more authentication options to Azure processors - Add credential configuration strategy property with from properties, default credentials, managed identity, and workload identity options - Add managed identity client id property - Do not allow SAS Token and Account Key to be used together BREAKING CHANGE: removing the old `Managed Identity Credentials` property in favor of the new `Credential Configuration Strategy` property which allows multiple new Azure authentication options Closes #2016 Signed-off-by: Marton Szasz <[email protected]> --- CONTROLLERS.md | 17 ++-- PROCESSORS.md | 102 +++++++++++---------- extensions/aws/processors/PutS3Object.h | 6 +- .../AzureStorageCredentialsService.cpp | 13 ++- .../AzureStorageCredentialsService.h | 28 ++++-- .../processors/AzureBlobStorageProcessorBase.cpp | 13 ++- .../processors/AzureBlobStorageProcessorBase.h | 32 +++++-- .../azure/processors/FetchAzureBlobStorage.h | 2 +- .../azure/processors/FetchAzureDataLakeStorage.h | 2 +- .../azure/processors/PutAzureDataLakeStorage.cpp | 13 +-- .../azure/storage/AzureBlobStorageClient.cpp | 10 +- .../azure/storage/AzureDataLakeStorageClient.cpp | 10 +- .../azure/storage/AzureStorageCredentials.cpp | 51 +++++++++-- extensions/azure/storage/AzureStorageCredentials.h | 17 +++- .../azure/tests/DeleteAzureBlobStorageTests.cpp | 74 ++++++++++++++- .../tests/DeleteAzureDataLakeStorageTests.cpp | 32 ++++++- .../azure/tests/FetchAzureBlobStorageTests.cpp | 74 ++++++++++++++- .../azure/tests/FetchAzureDataLakeStorageTests.cpp | 32 ++++++- .../azure/tests/ListAzureBlobStorageTests.cpp | 64 ++++++++++++- .../azure/tests/ListAzureDataLakeStorageTests.cpp | 42 ++++++++- .../azure/tests/PutAzureBlobStorageTests.cpp | 74 ++++++++++++++- .../azure/tests/PutAzureDataLakeStorageTests.cpp | 32 ++++++- extensions/azure/utils/AzureEnums.h | 28 ++++++ 23 files changed, 621 insertions(+), 147 deletions(-) diff --git a/CONTROLLERS.md b/CONTROLLERS.md index 52bf80d40..587470fdd 100644 --- a/CONTROLLERS.md +++ b/CONTROLLERS.md @@ -64,14 +64,15 @@ Manages the credentials for an Azure Storage account. This allows for multiple A In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered optional. The table also indicates any default values, and whether a property supports the NiFi Expression Language. -| Name | Default Value | Allowable Values | Description | -|----------------------------------------|---------------|------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Storage Account Name | | | The storage account name. | -| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.<br/>**Sensitive Property: true** | -| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.<br/>**Sensitive Property: true** | -| Common Storage Account Endpoint Suffix | | | 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). | -| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used. | -| **Use Managed Identity Credentials** | false | true<br/>false | If true Managed Identity credentials will be used together with the Storage Account Name for authentication. | +| Name | Default Value | Allowable Values | Description [...] +|----------------------------------------|-----------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...] +| Storage Account Name | | | The storage account name. [...] +| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.<br/>**Sensitive Property: true* [...] +| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.<br/>**Sensitive Property: true** [...] +| Common Storage Account Endpoint Suffix | | | 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). [...] +| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Credential Configuration Strategy is set to From Properties. [...] +| **Credential Configuration Strategy** | From Properties | From Properties<br/>Default Credential<br/>Managed Identity<br/>Workload Identity | The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, and Connection String properties. In other cases, the selected Azure identity source is used. [...] +| Managed Identity Client ID | | | Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.<br/>**Sensitive Property [...] ## CouchbaseClusterService diff --git a/PROCESSORS.md b/PROCESSORS.md index 5204c2f67..cd4b7141a 100644 --- a/PROCESSORS.md +++ b/PROCESSORS.md @@ -543,18 +543,19 @@ Deletes the provided blob from Azure Storage In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered optional. The table also indicates any default values, and whether a property supports the NiFi Expression Language. -| Name | Default Value | Allowable Values | Description | -|----------------------------------------|---------------|------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. | -| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** | -| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** | -| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| Common Storage Account Endpoint Suffix | | | 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). <br/>**Supports Expression Language: true** | -| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used.<br/>**Supports Expression Language: true** | -| **Use Managed Identity Credentials** | false | true<br/>false | If true Managed Identity credentials will be used together with the Storage Account Name for authentication. | -| Blob | | | The filename of the blob. If left empty the filename attribute will be used by default.<br/>**Supports Expression Language: true** | -| **Delete Snapshots Option** | None | None<br/>Include Snapshots<br/>Delete Snapshots Only | Specifies the snapshot deletion options to be used when deleting a blob. None: Deletes the blob only. Include Snapshots: Delete the blob and its snapshots. Delete Snapshots Only: Delete only the blob's snapshots. | +| Name | Default Value | Allowable Values | Description [...] +|----------------------------------------|-----------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...] +| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. [...] +| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** [...] +| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** [...] +| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.<br/>**Sensitive Property: true* [...] +| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** [...] +| Common Storage Account Endpoint Suffix | | | 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).<br/>**Supports Expression Language: true** [...] +| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Credential Configuration Strategy is set to From Properties.<br/>**Supports Expression Language: true** [...] +| **Credential Configuration Strategy** | From Properties | From Properties<br/>Default Credential<br/>Managed Identity<br/>Workload Identity | The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, and Connection String properties. In other cases, the selected Azure identity source is used. [...] +| Managed Identity Client ID | | | Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.<br/>**Sensitive Property [...] +| Blob | | | The filename of the blob. If left empty the filename attribute will be used by default.<br/>**Supports Expression Language: true** [...] +| **Delete Snapshots Option** | None | None<br/>Include Snapshots<br/>Delete Snapshots Only | Specifies the snapshot deletion options to be used when deleting a blob. None: Deletes the blob only. Include Snapshots: Delete the blob and its snapshots. Delete Snapshots Only: Delete only the blob's snapshots. [...] ### Relationships @@ -834,19 +835,20 @@ Retrieves contents of an Azure Storage Blob, writing the contents to the content In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered optional. The table also indicates any default values, and whether a property supports the NiFi Expression Language. -| Name | Default Value | Allowable Values | Description | -|----------------------------------------|---------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. | -| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** | -| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** | -| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| Common Storage Account Endpoint Suffix | | | 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). <br/>**Supports Expression Language: true** | -| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used.<br/>**Supports Expression Language: true** | -| **Use Managed Identity Credentials** | false | true<br/>false | If true Managed Identity credentials will be used together with the Storage Account Name for authentication. | -| Blob | | | The filename of the blob. If left empty the filename attribute will be used by default.<br/>**Supports Expression Language: true** | -| Range Start | | | The byte position at which to start reading from the blob. An empty value or a value of zero will start reading at the beginning of the blob.<br/>**Supports Expression Language: true** | -| Range Length | | | The number of bytes to download from the blob, starting from the Range Start. An empty value or a value that extends beyond the end of the blob will read to the end of the blob.<br/>**Supports Expression Language: true** | +| Name | Default Value | Allowable Values | Description [...] +|----------------------------------------|-----------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...] +| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. [...] +| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** [...] +| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** [...] +| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.<br/>**Sensitive Property: true* [...] +| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** [...] +| Common Storage Account Endpoint Suffix | | | 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).<br/>**Supports Expression Language: true** [...] +| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Credential Configuration Strategy is set to From Properties.<br/>**Supports Expression Language: true** [...] +| **Credential Configuration Strategy** | From Properties | From Properties<br/>Default Credential<br/>Managed Identity<br/>Workload Identity | The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, and Connection String properties. In other cases, the selected Azure identity source is used. [...] +| Managed Identity Client ID | | | Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.<br/>**Sensitive Property [...] +| Blob | | | The filename of the blob. If left empty the filename attribute will be used by default.<br/>**Supports Expression Language: true** [...] +| Range Start | | | The byte position at which to start reading from the blob. An empty value or a value of zero will start reading at the beginning of the blob.<br/>**Supports Expression Language: true** [...] +| Range Length | | | The number of bytes to download from the blob, starting from the Range Start. An empty value or a value that extends beyond the end of the blob will read to the end of the blob.<br/>**Supports Expression Language: true** [...] ### Relationships @@ -1400,18 +1402,19 @@ Lists blobs in an Azure Storage container. Listing details are attached to an em In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered optional. The table also indicates any default values, and whether a property supports the NiFi Expression Language. -| Name | Default Value | Allowable Values | Description | -|----------------------------------------|---------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. | -| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** | -| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** | -| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| Common Storage Account Endpoint Suffix | | | 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). <br/>**Supports Expression Language: true** | -| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used.<br/>**Supports Expression Language: true** | -| **Use Managed Identity Credentials** | false | true<br/>false | If true Managed Identity credentials will be used together with the Storage Account Name for authentication. | -| **Listing Strategy** | timestamps | none<br/>timestamps | Specify how to determine new/updated entities. If 'timestamps' is selected it tracks the latest timestamp of listed entity to determine new/updated entities. If 'none' is selected it lists an entity without any tracking, the same entity will be listed each time on executing this processor. | -| Prefix | | | Search prefix for listing<br/>**Supports Expression Language: true** | +| Name | Default Value | Allowable Values | Description [...] +|----------------------------------------|-----------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...] +| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. [...] +| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** [...] +| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** [...] +| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.<br/>**Sensitive Property: true* [...] +| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** [...] +| Common Storage Account Endpoint Suffix | | | 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).<br/>**Supports Expression Language: true** [...] +| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Credential Configuration Strategy is set to From Properties.<br/>**Supports Expression Language: true** [...] +| **Credential Configuration Strategy** | From Properties | From Properties<br/>Default Credential<br/>Managed Identity<br/>Workload Identity | The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, and Connection String properties. In other cases, the selected Azure identity source is used. [...] +| Managed Identity Client ID | | | Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.<br/>**Sensitive Property [...] +| **Listing Strategy** | timestamps | none<br/>timestamps | Specify how to determine new/updated entities. If 'timestamps' is selected it tracks the latest timestamp of listed entity to determine new/updated entities. If 'none' is selected it lists an entity without any tracking, the same entity will be listed each time on executing this processor. [...] +| Prefix | | | Search prefix for listing<br/>**Supports Expression Language: true** [...] ### Relationships @@ -2325,18 +2328,19 @@ Puts content into an Azure Storage Blob In the list below, the names of required properties appear in bold. Any other properties (not in bold) are considered optional. The table also indicates any default values, and whether a property supports the NiFi Expression Language. -| Name | Default Value | Allowable Values | Description | -|----------------------------------------|---------------|------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. | -| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** | -| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** | -| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** | -| Common Storage Account Endpoint Suffix | | | 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). <br/>**Supports Expression Language: true** | -| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used.<br/>**Supports Expression Language: true** | -| **Use Managed Identity Credentials** | false | true<br/>false | If true Managed Identity credentials will be used together with the Storage Account Name for authentication. | -| Blob | | | The filename of the blob. If left empty the filename attribute will be used by default.<br/>**Supports Expression Language: true** | -| **Create Container** | false | true<br/>false | Specifies whether to check if the container exists and to automatically create it if it does not. Permission to list containers is required. If false, this check is not made, but the Put operation will fail if the container does not exist. | +| Name | Default Value | Allowable Values | Description [...] +|----------------------------------------|-----------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ [...] +| Azure Storage Credentials Service | | | Name of the Azure Storage Credentials Service used to retrieve the connection string from. [...] +| **Container Name** | | | Name of the Azure Storage container. In case of PutAzureBlobStorage processor, container can be created if it does not exist.<br/>**Supports Expression Language: true** [...] +| Storage Account Name | | | The storage account name.<br/>**Supports Expression Language: true** [...] +| Storage Account Key | | | The storage account key. This is an admin-like password providing access to every container in this account. It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.<br/>**Sensitive Property: true* [...] +| SAS Token | | | Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.<br/>**Sensitive Property: true**<br/>**Supports Expression Language: true** [...] +| Common Storage Account Endpoint Suffix | | | 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).<br/>**Supports Expression Language: true** [...] +| Connection String | | | Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Credential Configuration Strategy is set to From Properties.<br/>**Supports Expression Language: true** [...] +| **Credential Configuration Strategy** | From Properties | From Properties<br/>Default Credential<br/>Managed Identity<br/>Workload Identity | The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, and Connection String properties. In other cases, the selected Azure identity source is used. [...] +| Managed Identity Client ID | | | Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.<br/>**Sensitive Property [...] +| Blob | | | The filename of the blob. If left empty the filename attribute will be used by default.<br/>**Supports Expression Language: true** [...] +| **Create Container** | false | true<br/>false | Specifies whether to check if the container exists and to automatically create it if it does not. Permission to list containers is required. If false, this check is not made, but the Put operation will fail if the container does not exist. [...] ### Relationships diff --git a/extensions/aws/processors/PutS3Object.h b/extensions/aws/processors/PutS3Object.h index 5fe86bf0d..912c45b7d 100644 --- a/extensions/aws/processors/PutS3Object.h +++ b/extensions/aws/processors/PutS3Object.h @@ -119,21 +119,21 @@ class PutS3Object : public S3Processor { // NOLINT(cppcoreguidelines-special-me .build(); EXTENSIONAPI static constexpr auto MultipartThreshold = core::PropertyDefinitionBuilder<>::createProperty("Multipart Threshold") .withDescription("Specifies the file size threshold for switch from the PutS3Object API to the PutS3MultipartUpload API. " - "Flow files bigger than this limit will be sent using the multipart process. The valid range is 5MB to 5GB.") + "Flow files bigger than this limit will be sent using the multipart process. The valid range is 5MB to 5GB.") .withValidator(core::StandardPropertyValidators::DATA_SIZE_VALIDATOR) .withDefaultValue("5 GB") .isRequired(true) .build(); EXTENSIONAPI static constexpr auto MultipartPartSize = core::PropertyDefinitionBuilder<>::createProperty("Multipart Part Size") .withDescription("Specifies the part size for use when the PutS3Multipart Upload API is used. " - "Flow files will be broken into chunks of this size for the upload process, but the last part sent can be smaller since it is not padded. The valid range is 5MB to 5GB.") + "Flow files will be broken into chunks of this size for the upload process, but the last part sent can be smaller since it is not padded. The valid range is 5MB to 5GB.") .withValidator(core::StandardPropertyValidators::DATA_SIZE_VALIDATOR) .withDefaultValue("5 GB") .isRequired(true) .build(); EXTENSIONAPI static constexpr auto MultipartUploadAgeOffInterval = core::PropertyDefinitionBuilder<>::createProperty("Multipart Upload AgeOff Interval") .withDescription("Specifies the interval at which existing multipart uploads in AWS S3 will be evaluated for ageoff. " - "When processor is triggered it will initiate the ageoff evaluation if this interval has been exceeded.") + "When processor is triggered it will initiate the ageoff evaluation if this interval has been exceeded.") .withValidator(core::StandardPropertyValidators::TIME_PERIOD_VALIDATOR) .withDefaultValue("60 min") .isRequired(true) diff --git a/extensions/azure/controllerservices/AzureStorageCredentialsService.cpp b/extensions/azure/controllerservices/AzureStorageCredentialsService.cpp index 4d0ae2b39..4f128f8f7 100644 --- a/extensions/azure/controllerservices/AzureStorageCredentialsService.cpp +++ b/extensions/azure/controllerservices/AzureStorageCredentialsService.cpp @@ -21,6 +21,7 @@ #include <set> #include "core/Resource.h" +#include "minifi-cpp/Exception.h" namespace org::apache::nifi::minifi::azure::controllers { @@ -29,6 +30,12 @@ void AzureStorageCredentialsService::initialize() { } void AzureStorageCredentialsService::onEnable() { + auto credential_configuration_strategy_str = getProperty(CredentialConfigurationStrategy.name).value_or(std::string{magic_enum::enum_name(CredentialConfigurationStrategyOption::FromProperties)}); + if (auto credential_configuration_strategy = magic_enum::enum_cast<CredentialConfigurationStrategyOption>(credential_configuration_strategy_str)) { + credentials_.setCredentialConfigurationStrategy(*credential_configuration_strategy); + } else { + throw minifi::Exception(ExceptionType::PROCESS_SCHEDULE_EXCEPTION, "Invalid Credential Configuration Strategy: " + credential_configuration_strategy_str); + } if (auto storage_account_name = getProperty(StorageAccountName.name)) { credentials_.setStorageAccountName(*storage_account_name); } @@ -39,13 +46,13 @@ void AzureStorageCredentialsService::onEnable() { credentials_.setSasToken(*sas_token); } if (auto common_storage_account_endpoint_suffix = getProperty(CommonStorageAccountEndpointSuffix.name)) { - credentials_.setEndpontSuffix(*common_storage_account_endpoint_suffix); + credentials_.setEndpointSuffix(*common_storage_account_endpoint_suffix); } if (auto connection_String = getProperty(ConnectionString.name)) { credentials_.setConnectionString(*connection_String); } - if (auto use_managed_identity_credentials = getProperty(UseManagedIdentityCredentials.name) | utils::andThen(parsing::parseBool)) { - credentials_.setUseManagedIdentityCredentials(*use_managed_identity_credentials); + if (auto managed_identity_client_id = getProperty(ManagedIdentityClientId.name)) { + credentials_.setManagedIdentityClientId(*managed_identity_client_id); } } diff --git a/extensions/azure/controllerservices/AzureStorageCredentialsService.h b/extensions/azure/controllerservices/AzureStorageCredentialsService.h index 0b655edcb..4662bd630 100644 --- a/extensions/azure/controllerservices/AzureStorageCredentialsService.h +++ b/extensions/azure/controllerservices/AzureStorageCredentialsService.h @@ -29,6 +29,7 @@ #include "minifi-cpp/core/PropertyValidator.h" #include "storage/AzureStorageCredentials.h" #include "utils/Export.h" +#include "utils/AzureEnums.h" namespace org::apache::nifi::minifi::azure::controllers { @@ -42,11 +43,13 @@ class AzureStorageCredentialsService : public core::controller::ControllerServic .build(); EXTENSIONAPI static constexpr auto StorageAccountKey = core::PropertyDefinitionBuilder<>::createProperty("Storage Account Key") .withDescription("The storage account key. This is an admin-like password providing access to every container in this account. " - "It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.") + "It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies " + "if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.") .isSensitive(true) .build(); EXTENSIONAPI static constexpr auto SASToken = core::PropertyDefinitionBuilder<>::createProperty("SAS Token") - .withDescription("Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.") + .withDescription("Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name " + "if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.") .isSensitive(true) .build(); EXTENSIONAPI static constexpr auto CommonStorageAccountEndpointSuffix = core::PropertyDefinitionBuilder<>::createProperty("Common Storage Account Endpoint Suffix") @@ -54,13 +57,21 @@ class AzureStorageCredentialsService : public core::controller::ControllerServic "different suffix in certain circumstances (like Azure Stack or non-public Azure regions).") .build(); EXTENSIONAPI static constexpr auto ConnectionString = core::PropertyDefinitionBuilder<>::createProperty("Connection String") - .withDescription("Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used.") + .withDescription("Connection string used to connect to Azure Storage service. This overrides all other set credential properties " + "if Credential Configuration Strategy is set to From Properties.") .build(); - EXTENSIONAPI static constexpr auto UseManagedIdentityCredentials = core::PropertyDefinitionBuilder<>::createProperty("Use Managed Identity Credentials") - .withDescription("If true Managed Identity credentials will be used together with the Storage Account Name for authentication.") + EXTENSIONAPI static constexpr auto CredentialConfigurationStrategy = + core::PropertyDefinitionBuilder<magic_enum::enum_count<CredentialConfigurationStrategyOption>()>::createProperty("Credential Configuration Strategy") + .withDescription("The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, " + "and Connection String properties. In other cases, the selected Azure identity source is used.") .isRequired(true) - .withValidator(core::StandardPropertyValidators::BOOLEAN_VALIDATOR) - .withDefaultValue("false") + .withDefaultValue(magic_enum::enum_name(CredentialConfigurationStrategyOption::FromProperties)) + .withAllowedValues(magic_enum::enum_names<CredentialConfigurationStrategyOption>()) + .build(); + EXTENSIONAPI static constexpr auto ManagedIdentityClientId = core::PropertyDefinitionBuilder<>::createProperty("Managed Identity Client ID") + .withDescription("Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities " + "are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.") + .isSensitive(true) .build(); EXTENSIONAPI static constexpr auto Properties = std::to_array<core::PropertyReference>({ StorageAccountName, @@ -68,7 +79,8 @@ class AzureStorageCredentialsService : public core::controller::ControllerServic SASToken, CommonStorageAccountEndpointSuffix, ConnectionString, - UseManagedIdentityCredentials + CredentialConfigurationStrategy, + ManagedIdentityClientId }); diff --git a/extensions/azure/processors/AzureBlobStorageProcessorBase.cpp b/extensions/azure/processors/AzureBlobStorageProcessorBase.cpp index f0eee83a7..88ae2a2e9 100644 --- a/extensions/azure/processors/AzureBlobStorageProcessorBase.cpp +++ b/extensions/azure/processors/AzureBlobStorageProcessorBase.cpp @@ -35,10 +35,10 @@ void AzureBlobStorageProcessorBase::onSchedule(core::ProcessContext& context, co return; } - use_managed_identity_credentials_ = minifi::utils::parseBoolProperty(context, UseManagedIdentityCredentials); + credential_configuration_strategy_ = minifi::utils::parseEnumProperty<CredentialConfigurationStrategyOption>(context, CredentialConfigurationStrategy); - if (use_managed_identity_credentials_) { - logger_->log_info("Using Managed Identity for authentication"); + if (credential_configuration_strategy_ != CredentialConfigurationStrategyOption::FromProperties) { + logger_->log_info("Using {} for authentication", magic_enum::enum_name(credential_configuration_strategy_)); return; } @@ -66,6 +66,7 @@ void AzureBlobStorageProcessorBase::onSchedule(core::ProcessContext& context, co storage::AzureStorageCredentials AzureBlobStorageProcessorBase::getAzureCredentialsFromProperties( core::ProcessContext &context, const core::FlowFile* const flow_file) const { storage::AzureStorageCredentials credentials; + credentials.setCredentialConfigurationStrategy(credential_configuration_strategy_); if (const auto value = context.getProperty(StorageAccountName, flow_file)) { credentials.setStorageAccountName(*value); } @@ -76,12 +77,14 @@ storage::AzureStorageCredentials AzureBlobStorageProcessorBase::getAzureCredenti credentials.setSasToken(*value); } if (const auto value = context.getProperty(CommonStorageAccountEndpointSuffix, flow_file)) { - credentials.setEndpontSuffix(*value); + credentials.setEndpointSuffix(*value); } if (const auto value = context.getProperty(ConnectionString, flow_file)) { credentials.setConnectionString(*value); } - credentials.setUseManagedIdentityCredentials(use_managed_identity_credentials_); + if (const auto value = context.getProperty(ManagedIdentityClientId, flow_file)) { + credentials.setManagedIdentityClientId(*value); + } return credentials; } diff --git a/extensions/azure/processors/AzureBlobStorageProcessorBase.h b/extensions/azure/processors/AzureBlobStorageProcessorBase.h index 32eb7f793..88c09bd75 100644 --- a/extensions/azure/processors/AzureBlobStorageProcessorBase.h +++ b/extensions/azure/processors/AzureBlobStorageProcessorBase.h @@ -50,29 +50,40 @@ class AzureBlobStorageProcessorBase : public AzureStorageProcessorBase { .build(); EXTENSIONAPI static constexpr auto StorageAccountKey = core::PropertyDefinitionBuilder<>::createProperty("Storage Account Key") .withDescription("The storage account key. This is an admin-like password providing access to every container in this account. " - "It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies.") + "It is recommended one uses Shared Access Signature (SAS) token instead for fine-grained control with policies " + "if Credential Configuration Strategy is set to From Properties. If set, SAS Token must be empty.") .supportsExpressionLanguage(true) .isSensitive(true) .build(); EXTENSIONAPI static constexpr auto SASToken = core::PropertyDefinitionBuilder<>::createProperty("SAS Token") - .withDescription("Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name if Managed Identity is not used.") + .withDescription("Shared Access Signature token. Specify either SAS Token (recommended) or Storage Account Key together with Storage Account Name " + "if Credential Configuration Strategy is set to From Properties. If set, Storage Account Key must be empty.") .supportsExpressionLanguage(true) .isSensitive(true) .build(); EXTENSIONAPI static constexpr auto CommonStorageAccountEndpointSuffix = core::PropertyDefinitionBuilder<>::createProperty("Common Storage Account Endpoint Suffix") .withDescription("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). ") + "different suffix in certain circumstances (like Azure Stack or non-public Azure regions).") .supportsExpressionLanguage(true) .build(); EXTENSIONAPI static constexpr auto ConnectionString = core::PropertyDefinitionBuilder<>::createProperty("Connection String") - .withDescription("Connection string used to connect to Azure Storage service. This overrides all other set credential properties if Managed Identity is not used.") + .withDescription("Connection string used to connect to Azure Storage service. This overrides all other set credential properties " + "if Credential Configuration Strategy is set to From Properties.") .supportsExpressionLanguage(true) .build(); - EXTENSIONAPI static constexpr auto UseManagedIdentityCredentials = core::PropertyDefinitionBuilder<>::createProperty("Use Managed Identity Credentials") - .withDescription("If true Managed Identity credentials will be used together with the Storage Account Name for authentication.") + EXTENSIONAPI static constexpr auto CredentialConfigurationStrategy = + core::PropertyDefinitionBuilder<magic_enum::enum_count<CredentialConfigurationStrategyOption>()>::createProperty("Credential Configuration Strategy") + .withDescription("The strategy to use for credential configuration. If set to From Properties, the credentials are parsed from the SAS Token, Storage Account Key, " + "and Connection String properties. In other cases, the selected Azure identity source is used.") .isRequired(true) - .withValidator(core::StandardPropertyValidators::BOOLEAN_VALIDATOR) - .withDefaultValue("false") + .withDefaultValue(magic_enum::enum_name(CredentialConfigurationStrategyOption::FromProperties)) + .withAllowedValues(magic_enum::enum_names<CredentialConfigurationStrategyOption>()) + .build(); + EXTENSIONAPI static constexpr auto ManagedIdentityClientId = core::PropertyDefinitionBuilder<>::createProperty("Managed Identity Client ID") + .withDescription("Client ID of the managed identity. The property is required when User Assigned Managed Identity is used for authentication and multiple user-assigned identities " + "are added to the resource. It must be empty in case of System Assigned Managed Identity and can also be left empty if only one user-assigned identity is present.") + .isSensitive(true) + .supportsExpressionLanguage(true) .build(); EXTENSIONAPI static constexpr auto Properties = utils::array_cat(AzureStorageProcessorBase::Properties, std::to_array<core::PropertyReference>({ ContainerName, @@ -81,7 +92,8 @@ class AzureBlobStorageProcessorBase : public AzureStorageProcessorBase { SASToken, CommonStorageAccountEndpointSuffix, ConnectionString, - UseManagedIdentityCredentials + CredentialConfigurationStrategy, + ManagedIdentityClientId })); @@ -105,7 +117,7 @@ class AzureBlobStorageProcessorBase : public AzureStorageProcessorBase { const core::FlowFile* const flow_file); storage::AzureBlobStorage azure_blob_storage_; - bool use_managed_identity_credentials_ = false; + CredentialConfigurationStrategyOption credential_configuration_strategy_ = CredentialConfigurationStrategyOption::FromProperties; }; } // namespace org::apache::nifi::minifi::azure::processors diff --git a/extensions/azure/processors/FetchAzureBlobStorage.h b/extensions/azure/processors/FetchAzureBlobStorage.h index 3270becc4..c9e1ee601 100644 --- a/extensions/azure/processors/FetchAzureBlobStorage.h +++ b/extensions/azure/processors/FetchAzureBlobStorage.h @@ -48,7 +48,7 @@ class FetchAzureBlobStorage final : public AzureBlobStorageSingleBlobProcessorBa EXTENSIONAPI static constexpr auto RangeLength = core::PropertyDefinitionBuilder<>::createProperty("Range Length") .withDescription("The number of bytes to download from the blob, starting from the Range Start. " - "An empty value or a value that extends beyond the end of the blob will read to the end of the blob.") + "An empty value or a value that extends beyond the end of the blob will read to the end of the blob.") .supportsExpressionLanguage(true) .build(); EXTENSIONAPI static constexpr auto Properties = utils::array_cat(AzureBlobStorageSingleBlobProcessorBase::Properties, std::to_array<core::PropertyReference>({ diff --git a/extensions/azure/processors/FetchAzureDataLakeStorage.h b/extensions/azure/processors/FetchAzureDataLakeStorage.h index bb9c4d338..f7ab3a11d 100644 --- a/extensions/azure/processors/FetchAzureDataLakeStorage.h +++ b/extensions/azure/processors/FetchAzureDataLakeStorage.h @@ -42,7 +42,7 @@ class FetchAzureDataLakeStorage final : public AzureDataLakeStorageFileProcessor .build(); EXTENSIONAPI static constexpr auto RangeLength = core::PropertyDefinitionBuilder<>::createProperty("Range Length") .withDescription("The number of bytes to download from the object, starting from the Range Start. " - "An empty value or a value that extends beyond the end of the object will read to the end of the object.") + "An empty value or a value that extends beyond the end of the object will read to the end of the object.") .supportsExpressionLanguage(true) .build(); EXTENSIONAPI static constexpr auto NumberOfRetries = core::PropertyDefinitionBuilder<>::createProperty("Number of Retries") diff --git a/extensions/azure/processors/PutAzureDataLakeStorage.cpp b/extensions/azure/processors/PutAzureDataLakeStorage.cpp index 00f4874cf..c1531de2c 100644 --- a/extensions/azure/processors/PutAzureDataLakeStorage.cpp +++ b/extensions/azure/processors/PutAzureDataLakeStorage.cpp @@ -36,17 +36,6 @@ void PutAzureDataLakeStorage::initialize() { void PutAzureDataLakeStorage::onSchedule(core::ProcessContext& context, core::ProcessSessionFactory& session_factory) { AzureDataLakeStorageFileProcessorBase::onSchedule(context, session_factory); - std::optional<storage::AzureStorageCredentials> credentials; - std::tie(std::ignore, credentials) = getCredentialsFromControllerService(context); - if (!credentials) { - throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Azure Storage Credentials Service property missing or invalid"); - } - - if (!credentials->isValid()) { - throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Azure Storage Credentials Service properties are not set or invalid"); - } - - credentials_ = *credentials; conflict_resolution_strategy_ = utils::parseEnumProperty<azure::FileExistsResolutionStrategy>(context, ConflictResolutionStrategy); } @@ -123,7 +112,7 @@ int64_t PutAzureDataLakeStorage::ReadCallback::operator()(const std::shared_ptr< } result_ = azure_data_lake_storage_.uploadFile(params_, buffer); - return read_ret; + return gsl::narrow<int64_t>(read_ret); } REGISTER_RESOURCE(PutAzureDataLakeStorage, Processor); diff --git a/extensions/azure/storage/AzureBlobStorageClient.cpp b/extensions/azure/storage/AzureBlobStorageClient.cpp index 9393008ba..be6186a7f 100644 --- a/extensions/azure/storage/AzureBlobStorageClient.cpp +++ b/extensions/azure/storage/AzureBlobStorageClient.cpp @@ -56,13 +56,13 @@ AzureBlobStorageClient::AzureBlobStorageClient() { } Azure::Storage::Blobs::BlobContainerClient AzureBlobStorageClient::createClient(const AzureStorageCredentials &credentials, const std::string &container_name) { - if (credentials.getUseManagedIdentityCredentials()) { - auto storage_client = Azure::Storage::Blobs::BlobServiceClient( - "https://" + credentials.getStorageAccountName() + ".blob." + credentials.getEndpointSuffix(), std::make_shared<Azure::Identity::ManagedIdentityCredential>()); - return storage_client.GetBlobContainerClient(container_name); - } else { + if (credentials.getCredentialConfigurationStrategy() == CredentialConfigurationStrategyOption::FromProperties) { return Azure::Storage::Blobs::BlobContainerClient::CreateFromConnectionString(credentials.buildConnectionString(), container_name); } + + auto storage_client = Azure::Storage::Blobs::BlobServiceClient("https://" + credentials.getStorageAccountName() + ".blob." + credentials.getEndpointSuffix(), + credentials.createAzureTokenCredential()); + return storage_client.GetBlobContainerClient(container_name); } bool AzureBlobStorageClient::createContainerIfNotExists(const PutAzureBlobStorageParameters& params) { diff --git a/extensions/azure/storage/AzureDataLakeStorageClient.cpp b/extensions/azure/storage/AzureDataLakeStorageClient.cpp index 7f588139f..d7618818e 100644 --- a/extensions/azure/storage/AzureDataLakeStorageClient.cpp +++ b/extensions/azure/storage/AzureDataLakeStorageClient.cpp @@ -42,14 +42,14 @@ std::unique_ptr<Azure::Storage::Files::DataLake::DataLakeFileSystemClient> Azure options.Retry.MaxRetries = gsl::narrow<int32_t>(*number_of_retries); } - if (credentials.getUseManagedIdentityCredentials()) { - auto datalake_service_client = Azure::Storage::Files::DataLake::DataLakeServiceClient( - "https://" + credentials.getStorageAccountName() + ".dfs." + credentials.getEndpointSuffix(), std::make_shared<Azure::Identity::ManagedIdentityCredential>(), options); - return std::make_unique<Azure::Storage::Files::DataLake::DataLakeFileSystemClient>(datalake_service_client.GetFileSystemClient(file_system_name)); - } else { + if (credentials.getCredentialConfigurationStrategy() == CredentialConfigurationStrategyOption::FromProperties) { return std::make_unique<Azure::Storage::Files::DataLake::DataLakeFileSystemClient>( Azure::Storage::Files::DataLake::DataLakeFileSystemClient::CreateFromConnectionString(credentials.buildConnectionString(), file_system_name, options)); } + + auto datalake_service_client = Azure::Storage::Files::DataLake::DataLakeServiceClient( + "https://" + credentials.getStorageAccountName() + ".dfs." + credentials.getEndpointSuffix(), credentials.createAzureTokenCredential(), options); + return std::make_unique<Azure::Storage::Files::DataLake::DataLakeFileSystemClient>(datalake_service_client.GetFileSystemClient(file_system_name)); } Azure::Storage::Files::DataLake::DataLakeDirectoryClient AzureDataLakeStorageClient::getDirectoryClient(const AzureDataLakeStorageParameters& params) { diff --git a/extensions/azure/storage/AzureStorageCredentials.cpp b/extensions/azure/storage/AzureStorageCredentials.cpp index 070dfe860..7af832d22 100644 --- a/extensions/azure/storage/AzureStorageCredentials.cpp +++ b/extensions/azure/storage/AzureStorageCredentials.cpp @@ -34,7 +34,7 @@ void AzureStorageCredentials::setSasToken(const std::string& sas_token) { sas_token_ = sas_token; } -void AzureStorageCredentials::setEndpontSuffix(const std::string& endpoint_suffix) { +void AzureStorageCredentials::setEndpointSuffix(const std::string& endpoint_suffix) { endpoint_suffix_ = endpoint_suffix; } @@ -42,8 +42,12 @@ void AzureStorageCredentials::setConnectionString(const std::string& connection_ connection_string_ = connection_string; } -void AzureStorageCredentials::setUseManagedIdentityCredentials(bool use_managed_identity_credentials) { - use_managed_identity_credentials_ = use_managed_identity_credentials; +void AzureStorageCredentials::setCredentialConfigurationStrategy(CredentialConfigurationStrategyOption credential_configuration_strategy) { + credential_configuration_strategy_ = credential_configuration_strategy; +} + +void AzureStorageCredentials::setManagedIdentityClientId(const std::string& managed_identity_client_id) { + managed_identity_client_id_ = managed_identity_client_id; } std::string AzureStorageCredentials::getStorageAccountName() const { @@ -54,12 +58,16 @@ std::string AzureStorageCredentials::getEndpointSuffix() const { return endpoint_suffix_.empty() ? "core.windows.net" : endpoint_suffix_; } -bool AzureStorageCredentials::getUseManagedIdentityCredentials() const { - return use_managed_identity_credentials_; +CredentialConfigurationStrategyOption AzureStorageCredentials::getCredentialConfigurationStrategy() const { + return credential_configuration_strategy_; +} + +std::string AzureStorageCredentials::getManagedIdentityClientId() const { + return managed_identity_client_id_; } std::string AzureStorageCredentials::buildConnectionString() const { - if (use_managed_identity_credentials_) { + if (credential_configuration_strategy_ != CredentialConfigurationStrategyOption::FromProperties) { return ""; } @@ -67,6 +75,10 @@ std::string AzureStorageCredentials::buildConnectionString() const { return connection_string_; } + if (!storage_account_key_.empty() && !sas_token_.empty()) { + return ""; + } + if (storage_account_name_.empty() || (storage_account_key_.empty() && sas_token_.empty())) { return ""; } @@ -90,16 +102,35 @@ std::string AzureStorageCredentials::buildConnectionString() const { } bool AzureStorageCredentials::isValid() const { - return (getUseManagedIdentityCredentials() && !getStorageAccountName().empty()) || - (!getUseManagedIdentityCredentials() && !buildConnectionString().empty()); + return (credential_configuration_strategy_ != CredentialConfigurationStrategyOption::FromProperties && !getStorageAccountName().empty()) || + (credential_configuration_strategy_ == CredentialConfigurationStrategyOption::FromProperties && !buildConnectionString().empty()); +} + +std::shared_ptr<Azure::Core::Credentials::TokenCredential> AzureStorageCredentials::createAzureTokenCredential() const { + std::shared_ptr<Azure::Core::Credentials::TokenCredential> credential; + if (credential_configuration_strategy_ == CredentialConfigurationStrategyOption::ManagedIdentity) { + Azure::Identity::ManagedIdentityCredentialOptions options; + if (managed_identity_client_id_.empty()) { + options.IdentityId = Azure::Identity::ManagedIdentityId::SystemAssigned(); + } else { + options.IdentityId = Azure::Identity::ManagedIdentityId::FromUserAssignedClientId(managed_identity_client_id_); + } + credential = std::make_shared<Azure::Identity::ManagedIdentityCredential>(options); + } else if (credential_configuration_strategy_ == CredentialConfigurationStrategyOption::DefaultCredential) { + credential = std::make_shared<Azure::Identity::DefaultAzureCredential>(); + } else if (credential_configuration_strategy_ == CredentialConfigurationStrategyOption::WorkloadIdentity) { + credential = std::make_shared<Azure::Identity::WorkloadIdentityCredential>(); + } + + return credential; } bool AzureStorageCredentials::operator==(const AzureStorageCredentials& other) const { - if (other.use_managed_identity_credentials_ != use_managed_identity_credentials_) { + if (other.credential_configuration_strategy_ != credential_configuration_strategy_) { return false; } - if (use_managed_identity_credentials_) { + if (credential_configuration_strategy_ != CredentialConfigurationStrategyOption::FromProperties) { return storage_account_name_ == other.storage_account_name_ && endpoint_suffix_ == other.endpoint_suffix_; } else { return buildConnectionString() == other.buildConnectionString(); diff --git a/extensions/azure/storage/AzureStorageCredentials.h b/extensions/azure/storage/AzureStorageCredentials.h index ea14d2fb8..b156b5361 100644 --- a/extensions/azure/storage/AzureStorageCredentials.h +++ b/extensions/azure/storage/AzureStorageCredentials.h @@ -20,6 +20,10 @@ #pragma once #include <string> +#include <memory> + +#include "utils/AzureEnums.h" +#include "azure/identity.hpp" namespace org::apache::nifi::minifi::azure::storage { @@ -28,16 +32,20 @@ class AzureStorageCredentials { void setStorageAccountName(const std::string& storage_account_name); void setStorageAccountKey(const std::string& storage_account_key); void setSasToken(const std::string& sas_token); - void setEndpontSuffix(const std::string& endpoint_suffix); + void setEndpointSuffix(const std::string& endpoint_suffix); void setConnectionString(const std::string& connection_string); - void setUseManagedIdentityCredentials(bool use_managed_identity_credentials); + void setCredentialConfigurationStrategy(CredentialConfigurationStrategyOption credential_configuration_strategy); + void setManagedIdentityClientId(const std::string& managed_identity_client_id); std::string getStorageAccountName() const; std::string getEndpointSuffix() const; - bool getUseManagedIdentityCredentials() const; + CredentialConfigurationStrategyOption getCredentialConfigurationStrategy() const; + std::string getManagedIdentityClientId() const; std::string buildConnectionString() const; bool isValid() const; + std::shared_ptr<Azure::Core::Credentials::TokenCredential> createAzureTokenCredential() const; + bool operator==(const AzureStorageCredentials& other) const; private: @@ -46,7 +54,8 @@ class AzureStorageCredentials { std::string sas_token_; std::string endpoint_suffix_; std::string connection_string_; - bool use_managed_identity_credentials_ = false; + std::string managed_identity_client_id_; + CredentialConfigurationStrategyOption credential_configuration_strategy_ = CredentialConfigurationStrategyOption::FromProperties; }; } // namespace org::apache::nifi::minifi::azure::storage diff --git a/extensions/azure/tests/DeleteAzureBlobStorageTests.cpp b/extensions/azure/tests/DeleteAzureBlobStorageTests.cpp index 7c7d790fd..b554021bb 100644 --- a/extensions/azure/tests/DeleteAzureBlobStorageTests.cpp +++ b/extensions/azure/tests/DeleteAzureBlobStorageTests.cpp @@ -134,23 +134,61 @@ TEST_CASE_METHOD(DeleteAzureBlobStorageTestsFixture, "Test credentials settings" REQUIRE(failed_flowfiles[0] == TEST_DATA); } - SECTION("Account name and managed identity are used in properties") { + SECTION("Account name and Azure default identity sources are used in properties") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_blob_storage_processor_, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_blob_storage_processor_, "Credential Configuration Strategy", credential_configuration_strategy_string); + plan_->setProperty(azure_blob_storage_processor_, "Managed Identity Client ID", managed_identity_client_id); test_controller_.runSession(plan_, true); CHECK(getFailedFlowFileContents().empty()); auto passed_params = mock_blob_storage_ptr_->getPassedDeleteParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } - SECTION("Account name and managed identity are used from Azure Storage Credentials Service") { + SECTION("Account name and Azure default identity sources are used from Azure Storage Credentials Service") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_storage_cred_service, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_storage_cred_service, "Credential Configuration Strategy", credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service, "Common Storage Account Endpoint Suffix", "core.chinacloudapi.cn"); + plan_->setProperty(azure_storage_cred_service, "Managed Identity Client ID", managed_identity_client_id); plan_->setProperty(azure_blob_storage_processor_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); test_controller_.runSession(plan_, true); CHECK(getFailedFlowFileContents().empty()); @@ -158,6 +196,8 @@ TEST_CASE_METHOD(DeleteAzureBlobStorageTestsFixture, "Test credentials settings" CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.chinacloudapi.cn"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } @@ -209,6 +249,32 @@ TEST_CASE_METHOD(DeleteAzureBlobStorageTestsFixture, "Test credentials settings" REQUIRE(failed_flowfiles.size() == 1); REQUIRE(failed_flowfiles[0] == TEST_DATA); } + + SECTION("Both SAS Token and Storage Account Key cannot be set in credentials service") { + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); + plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_storage_cred_service, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_storage_cred_service, "SAS Token", SAS_TOKEN); + plan_->setProperty(azure_blob_storage_processor_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); + test_controller_.runSession(plan_, true); + auto passed_params = mock_blob_storage_ptr_->getPassedDeleteParams(); + REQUIRE(passed_params.credentials.buildConnectionString().empty()); + auto failed_flowfiles = getFailedFlowFileContents(); + REQUIRE(failed_flowfiles.size() == 1); + REQUIRE(failed_flowfiles[0] == TEST_DATA); + } + + SECTION("Both SAS Token and Storage Account Key cannot be set in properties") { + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_blob_storage_processor_, "SAS Token", SAS_TOKEN); + test_controller_.runSession(plan_, true); + auto passed_params = mock_blob_storage_ptr_->getPassedDeleteParams(); + REQUIRE(passed_params.credentials.buildConnectionString().empty()); + auto failed_flowfiles = getFailedFlowFileContents(); + REQUIRE(failed_flowfiles.size() == 1); + REQUIRE(failed_flowfiles[0] == TEST_DATA); + } } TEST_CASE_METHOD(DeleteAzureBlobStorageTestsFixture, "Test Azure blob delete failure in case Blob is not set and filename is empty", "[azureBlobStorageDelete]") { diff --git a/extensions/azure/tests/DeleteAzureDataLakeStorageTests.cpp b/extensions/azure/tests/DeleteAzureDataLakeStorageTests.cpp index 40bacdca5..a241429c2 100644 --- a/extensions/azure/tests/DeleteAzureDataLakeStorageTests.cpp +++ b/extensions/azure/tests/DeleteAzureDataLakeStorageTests.cpp @@ -54,19 +54,47 @@ TEST_CASE_METHOD(DeleteAzureDataLakeStorageTestsFixture, "Test Azure credentials CHECK(getFailedFlowFileContents().empty()); } -TEST_CASE_METHOD(DeleteAzureDataLakeStorageTestsFixture, "Test Azure credentials with managed identity use", "[azureDataLakeStorageParameters]") { +TEST_CASE_METHOD(DeleteAzureDataLakeStorageTestsFixture, "Test Azure credentials with Azure default identity sources", "[azureDataLakeStorageParameters]") { setDefaultProperties(); + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, "test"); - plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::UseManagedIdentityCredentials, "true"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::CredentialConfigurationStrategy, credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ManagedIdentityClientId, managed_identity_client_id); test_controller_.runSession(plan_, true); auto passed_params = mock_data_lake_storage_client_ptr_->getPassedDeleteParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == "TEST_ACCOUNT"); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(getFailedFlowFileContents().empty()); } +TEST_CASE_METHOD(DeleteAzureDataLakeStorageTestsFixture, "Both SAS Token and Storage Account Key cannot be set in credentials service") { + setDefaultProperties(); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, ""); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::SASToken, "token"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountKey, "TEST_KEY"); + REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); +} + TEST_CASE_METHOD(DeleteAzureDataLakeStorageTestsFixture, "Filesystem name is not set", "[azureDataLakeStorageParameters]") { plan_->setDynamicProperty(update_attribute_, "test.filesystemname", ""); test_controller_.runSession(plan_, true); diff --git a/extensions/azure/tests/FetchAzureBlobStorageTests.cpp b/extensions/azure/tests/FetchAzureBlobStorageTests.cpp index a1e901744..cd57961cb 100644 --- a/extensions/azure/tests/FetchAzureBlobStorageTests.cpp +++ b/extensions/azure/tests/FetchAzureBlobStorageTests.cpp @@ -130,23 +130,61 @@ TEST_CASE_METHOD(FetchAzureBlobStorageTestsFixture, "Test credentials settings", REQUIRE(failed_flowfiles[0] == TEST_DATA); } - SECTION("Account name and managed identity are used in properties") { + SECTION("Account name and Azure default identity sources are used in properties") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_blob_storage_processor_, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_blob_storage_processor_, "Credential Configuration Strategy", credential_configuration_strategy_string); + plan_->setProperty(azure_blob_storage_processor_, "Managed Identity Client ID", managed_identity_client_id); test_controller_.runSession(plan_, true); CHECK(getFailedFlowFileContents().empty()); auto passed_params = mock_blob_storage_ptr_->getPassedFetchParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } - SECTION("Account name and managed identity are used from Azure Storage Credentials Service") { + SECTION("Account name and Azure default identity sources are used from Azure Storage Credentials Service") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_storage_cred_service, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_storage_cred_service, "Credential Configuration Strategy", credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service, "Common Storage Account Endpoint Suffix", "core.chinacloudapi.cn"); + plan_->setProperty(azure_storage_cred_service, "Managed Identity Client ID", managed_identity_client_id); plan_->setProperty(azure_blob_storage_processor_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); test_controller_.runSession(plan_, true); CHECK(getFailedFlowFileContents().empty()); @@ -154,6 +192,8 @@ TEST_CASE_METHOD(FetchAzureBlobStorageTestsFixture, "Test credentials settings", CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.chinacloudapi.cn"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } @@ -205,6 +245,32 @@ TEST_CASE_METHOD(FetchAzureBlobStorageTestsFixture, "Test credentials settings", REQUIRE(failed_flowfiles.size() == 1); REQUIRE(failed_flowfiles[0] == TEST_DATA); } + + SECTION("Both SAS Token and Storage Account Key cannot be set in credentials service") { + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); + plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_storage_cred_service, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_storage_cred_service, "SAS Token", SAS_TOKEN); + plan_->setProperty(azure_blob_storage_processor_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); + test_controller_.runSession(plan_, true); + auto passed_params = mock_blob_storage_ptr_->getPassedFetchParams(); + REQUIRE(passed_params.credentials.buildConnectionString().empty()); + auto failed_flowfiles = getFailedFlowFileContents(); + REQUIRE(failed_flowfiles.size() == 1); + REQUIRE(failed_flowfiles[0] == TEST_DATA); + } + + SECTION("Both SAS Token and Storage Account Key cannot be set in properties") { + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_blob_storage_processor_, "SAS Token", SAS_TOKEN); + test_controller_.runSession(plan_, true); + auto passed_params = mock_blob_storage_ptr_->getPassedFetchParams(); + REQUIRE(passed_params.credentials.buildConnectionString().empty()); + auto failed_flowfiles = getFailedFlowFileContents(); + REQUIRE(failed_flowfiles.size() == 1); + REQUIRE(failed_flowfiles[0] == TEST_DATA); + } } TEST_CASE_METHOD(FetchAzureBlobStorageTestsFixture, "Test Azure blob fetch failure in case Blob is not set and filename is empty", "[azureBlobStorageFetch]") { diff --git a/extensions/azure/tests/FetchAzureDataLakeStorageTests.cpp b/extensions/azure/tests/FetchAzureDataLakeStorageTests.cpp index 70d63bc6d..c5505f49e 100644 --- a/extensions/azure/tests/FetchAzureDataLakeStorageTests.cpp +++ b/extensions/azure/tests/FetchAzureDataLakeStorageTests.cpp @@ -54,19 +54,47 @@ TEST_CASE_METHOD(FetchAzureDataLakeStorageTestsFixture, "Test Azure credentials CHECK(getFailedFlowFileContents().empty()); } -TEST_CASE_METHOD(FetchAzureDataLakeStorageTestsFixture, "Test Azure credentials with managed identity use", "[azureDataLakeStorageParameters]") { +TEST_CASE_METHOD(FetchAzureDataLakeStorageTestsFixture, "Test Azure credentials with Azure default identity sources", "[azureDataLakeStorageParameters]") { setDefaultProperties(); + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, "test"); - plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::UseManagedIdentityCredentials, "true"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::CredentialConfigurationStrategy, credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ManagedIdentityClientId, managed_identity_client_id); test_controller_.runSession(plan_, true); auto passed_params = mock_data_lake_storage_client_ptr_->getPassedFetchParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == "TEST_ACCOUNT"); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(getFailedFlowFileContents().empty()); } +TEST_CASE_METHOD(FetchAzureDataLakeStorageTestsFixture, "Both SAS Token and Storage Account Key cannot be set in credentials service") { + setDefaultProperties(); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, ""); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::SASToken, "token"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountKey, "TEST_KEY"); + REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); +} + TEST_CASE_METHOD(FetchAzureDataLakeStorageTestsFixture, "Filesystem name is not set", "[azureDataLakeStorageParameters]") { plan_->setDynamicProperty(update_attribute_, "test.filesystemname", ""); test_controller_.runSession(plan_, true); diff --git a/extensions/azure/tests/ListAzureBlobStorageTests.cpp b/extensions/azure/tests/ListAzureBlobStorageTests.cpp index 2717469ec..31bdad909 100644 --- a/extensions/azure/tests/ListAzureBlobStorageTests.cpp +++ b/extensions/azure/tests/ListAzureBlobStorageTests.cpp @@ -168,28 +168,68 @@ TEST_CASE_METHOD(ListAzureBlobStorageTestsFixture, "Test credentials settings", REQUIRE(passed_params.credentials.buildConnectionString() == CONNECTION_STRING); } - SECTION("Account name and managed identity are used in properties") { + SECTION("Account name and Azure default identity sources are used in properties") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + plan_->setProperty(list_azure_blob_storage_, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(list_azure_blob_storage_, "Use Managed Identity Credentials", "true"); + plan_->setProperty(list_azure_blob_storage_, "Credential Configuration Strategy", credential_configuration_strategy_string); + plan_->setProperty(list_azure_blob_storage_, "Managed Identity Client ID", managed_identity_client_id); test_controller_.runSession(plan_, true); auto passed_params = mock_blob_storage_ptr_->getPassedListParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } - SECTION("Account name and managed identity are used from Azure Storage Credentials Service") { + SECTION("Account name and Azure default identity sources are used from Azure Storage Credentials Service") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_storage_cred_service, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_storage_cred_service, "Credential Configuration Strategy", credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service, "Common Storage Account Endpoint Suffix", "core.chinacloudapi.cn"); + plan_->setProperty(azure_storage_cred_service, "Managed Identity Client ID", managed_identity_client_id); plan_->setProperty(list_azure_blob_storage_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); test_controller_.runSession(plan_, true); auto passed_params = mock_blob_storage_ptr_->getPassedListParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.chinacloudapi.cn"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } @@ -224,6 +264,22 @@ TEST_CASE_METHOD(ListAzureBlobStorageTestsFixture, "Test credentials settings", plan_->setProperty(list_azure_blob_storage_, "Storage Account Key", STORAGE_ACCOUNT_KEY); REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); } + + SECTION("Both SAS Token and Storage Account Key cannot be set in credentials service") { + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); + plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_storage_cred_service, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_storage_cred_service, "SAS Token", SAS_TOKEN); + plan_->setProperty(list_azure_blob_storage_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); + REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); + } + + SECTION("Both SAS Token and Storage Account Key cannot be set in properties") { + plan_->setProperty(list_azure_blob_storage_, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(list_azure_blob_storage_, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(list_azure_blob_storage_, "SAS Token", SAS_TOKEN); + REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); + } } TEST_CASE_METHOD(ListAzureBlobStorageTestsFixture, "List all files every time", "[ListAzureBlobStorage]") { diff --git a/extensions/azure/tests/ListAzureDataLakeStorageTests.cpp b/extensions/azure/tests/ListAzureDataLakeStorageTests.cpp index 4cc0fa5c7..4f2a19b51 100644 --- a/extensions/azure/tests/ListAzureDataLakeStorageTests.cpp +++ b/extensions/azure/tests/ListAzureDataLakeStorageTests.cpp @@ -43,7 +43,7 @@ class ListAzureDataLakeStorageTestsFixture { auto mock_data_lake_storage_client = std::make_unique<MockDataLakeStorageClient>(); mock_data_lake_storage_client_ptr_ = mock_data_lake_storage_client.get(); auto uuid = utils::IdGenerator::getIdGenerator()->generate(); - auto impl = std::unique_ptr<minifi::azure::processors::ListAzureDataLakeStorage>( + auto impl = std::unique_ptr<minifi::azure::processors::ListAzureDataLakeStorage>( // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks) new minifi::azure::processors::ListAzureDataLakeStorage({ .uuid = uuid, .name = "ListAzureDataLakeStorage", .logger = logging::LoggerFactory<minifi::azure::processors::ListAzureDataLakeStorage>::getLogger(uuid)}, std::move(mock_data_lake_storage_client))); @@ -216,4 +216,44 @@ TEST_CASE_METHOD(ListAzureDataLakeStorageTestsFixture, "Throw on invalid path fi REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); } +TEST_CASE_METHOD(ListAzureDataLakeStorageTestsFixture, "Test Azure credentials with Azure default identity sources", "[azureDataLakeStorageParameters]") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, "test"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::CredentialConfigurationStrategy, credential_configuration_strategy_string); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ManagedIdentityClientId, managed_identity_client_id); + test_controller_.runSession(plan_, true); + auto passed_params = mock_data_lake_storage_client_ptr_->getPassedListParams(); + CHECK(passed_params.credentials.buildConnectionString().empty()); + CHECK(passed_params.credentials.getStorageAccountName() == "TEST_ACCOUNT"); + CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); +} + +TEST_CASE_METHOD(ListAzureDataLakeStorageTestsFixture, "Both SAS Token and Storage Account Key cannot be set in credentials service") { + setDefaultProperties(); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, ""); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::SASToken, "token"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountKey, "TEST_KEY"); + REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); +} + } // namespace diff --git a/extensions/azure/tests/PutAzureBlobStorageTests.cpp b/extensions/azure/tests/PutAzureBlobStorageTests.cpp index 1b4e6cc7b..a4a0ed19e 100644 --- a/extensions/azure/tests/PutAzureBlobStorageTests.cpp +++ b/extensions/azure/tests/PutAzureBlobStorageTests.cpp @@ -134,23 +134,61 @@ TEST_CASE_METHOD(PutAzureBlobStorageTestsFixture, "Test credentials settings", " REQUIRE(failed_flowfiles[0] == TEST_DATA); } - SECTION("Account name and managed identity are used in properties") { + SECTION("Account name and Azure default identity sources are used in properties") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_blob_storage_processor_, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_blob_storage_processor_, "Credential Configuration Strategy", credential_configuration_strategy_string); + plan_->setProperty(azure_blob_storage_processor_, "Managed Identity Client ID", managed_identity_client_id); test_controller_.runSession(plan_, true); CHECK(getFailedFlowFileContents().empty()); auto passed_params = mock_blob_storage_ptr_->getPassedPutParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } - SECTION("Account name and managed identity are used from Azure Storage Credentials Service") { + SECTION("Account name and Azure default identity sources are used from Azure Storage Credentials Service") { + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); - plan_->setProperty(azure_storage_cred_service, "Use Managed Identity Credentials", "true"); + plan_->setProperty(azure_storage_cred_service, "Credential Configuration Strategy", credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service, "Common Storage Account Endpoint Suffix", "core.chinacloudapi.cn"); + plan_->setProperty(azure_storage_cred_service, "Managed Identity Client ID", managed_identity_client_id); plan_->setProperty(azure_blob_storage_processor_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); test_controller_.runSession(plan_, true); CHECK(getFailedFlowFileContents().empty()); @@ -158,6 +196,8 @@ TEST_CASE_METHOD(PutAzureBlobStorageTestsFixture, "Test credentials settings", " CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == STORAGE_ACCOUNT_NAME); CHECK(passed_params.credentials.getEndpointSuffix() == "core.chinacloudapi.cn"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); CHECK(passed_params.container_name == CONTAINER_NAME); } @@ -209,6 +249,32 @@ TEST_CASE_METHOD(PutAzureBlobStorageTestsFixture, "Test credentials settings", " REQUIRE(failed_flowfiles.size() == 1); REQUIRE(failed_flowfiles[0] == TEST_DATA); } + + SECTION("Both SAS Token and Storage Account Key cannot be set in credentials service") { + auto azure_storage_cred_service = plan_->addController("AzureStorageCredentialsService", "AzureStorageCredentialsService"); + plan_->setProperty(azure_storage_cred_service, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_storage_cred_service, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_storage_cred_service, "SAS Token", SAS_TOKEN); + plan_->setProperty(azure_blob_storage_processor_, "Azure Storage Credentials Service", "AzureStorageCredentialsService"); + test_controller_.runSession(plan_, true); + auto passed_params = mock_blob_storage_ptr_->getPassedPutParams(); + REQUIRE(passed_params.credentials.buildConnectionString().empty()); + auto failed_flowfiles = getFailedFlowFileContents(); + REQUIRE(failed_flowfiles.size() == 1); + REQUIRE(failed_flowfiles[0] == TEST_DATA); + } + + SECTION("Both SAS Token and Storage Account Key cannot be set in properties") { + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Name", STORAGE_ACCOUNT_NAME); + plan_->setProperty(azure_blob_storage_processor_, "Storage Account Key", STORAGE_ACCOUNT_KEY); + plan_->setProperty(azure_blob_storage_processor_, "SAS Token", SAS_TOKEN); + test_controller_.runSession(plan_, true); + auto passed_params = mock_blob_storage_ptr_->getPassedPutParams(); + REQUIRE(passed_params.credentials.buildConnectionString().empty()); + auto failed_flowfiles = getFailedFlowFileContents(); + REQUIRE(failed_flowfiles.size() == 1); + REQUIRE(failed_flowfiles[0] == TEST_DATA); + } } TEST_CASE_METHOD(PutAzureBlobStorageTestsFixture, "Test Azure blob upload failure in case Blob is not set and filename is empty", "[azureBlobStorageUpload]") { diff --git a/extensions/azure/tests/PutAzureDataLakeStorageTests.cpp b/extensions/azure/tests/PutAzureDataLakeStorageTests.cpp index 9ea94222d..6938dc841 100644 --- a/extensions/azure/tests/PutAzureDataLakeStorageTests.cpp +++ b/extensions/azure/tests/PutAzureDataLakeStorageTests.cpp @@ -54,19 +54,47 @@ TEST_CASE_METHOD(PutAzureDataLakeStorageTestsFixture, "Test Azure credentials wi REQUIRE(getFailedFlowFileContents().empty()); } -TEST_CASE_METHOD(PutAzureDataLakeStorageTestsFixture, "Test Azure credentials with managed identity use", "[azureDataLakeStorageParameters]") { +TEST_CASE_METHOD(PutAzureDataLakeStorageTestsFixture, "Test Azure credentials with Azure default identity sources", "[azureDataLakeStorageParameters]") { setDefaultProperties(); + minifi::azure::CredentialConfigurationStrategyOption expected_configuration_strategy_option{}; + std::string credential_configuration_strategy_string; + std::string managed_identity_client_id; + SECTION("Managed Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::ManagedIdentity; + credential_configuration_strategy_string = "Managed Identity"; + managed_identity_client_id = "test-managed-identity-client-id"; + } + SECTION("Default Credential") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::DefaultCredential; + credential_configuration_strategy_string = "Default Credential"; + } + SECTION("Workload Identity") { + expected_configuration_strategy_option = minifi::azure::CredentialConfigurationStrategyOption::WorkloadIdentity; + credential_configuration_strategy_string = "Workload Identity"; + } plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, "test"); - plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::UseManagedIdentityCredentials, "true"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::CredentialConfigurationStrategy, credential_configuration_strategy_string); plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ManagedIdentityClientId, managed_identity_client_id); test_controller_.runSession(plan_, true); auto passed_params = mock_data_lake_storage_client_ptr_->getPassedPutParams(); CHECK(passed_params.credentials.buildConnectionString().empty()); CHECK(passed_params.credentials.getStorageAccountName() == "TEST_ACCOUNT"); CHECK(passed_params.credentials.getEndpointSuffix() == "core.windows.net"); + CHECK(passed_params.credentials.getCredentialConfigurationStrategy() == expected_configuration_strategy_option); + CHECK(passed_params.credentials.getManagedIdentityClientId() == managed_identity_client_id); REQUIRE(getFailedFlowFileContents().empty()); } +TEST_CASE_METHOD(PutAzureDataLakeStorageTestsFixture, "Both SAS Token and Storage Account Key cannot be set in credentials service") { + setDefaultProperties(); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::ConnectionString, ""); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::SASToken, "token"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountName, "TEST_ACCOUNT"); + plan_->setProperty(azure_storage_cred_service_, minifi::azure::controllers::AzureStorageCredentialsService::StorageAccountKey, "TEST_KEY"); + REQUIRE_THROWS_AS(test_controller_.runSession(plan_, true), minifi::Exception); +} + TEST_CASE_METHOD(PutAzureDataLakeStorageTestsFixture, "Filesystem name is not set", "[azureDataLakeStorageParameters]") { plan_->setDynamicProperty(update_attribute_, "test.filesystemname", ""); test_controller_.runSession(plan_, true); diff --git a/extensions/azure/utils/AzureEnums.h b/extensions/azure/utils/AzureEnums.h index 28a54535b..ab87836e1 100644 --- a/extensions/azure/utils/AzureEnums.h +++ b/extensions/azure/utils/AzureEnums.h @@ -19,6 +19,8 @@ */ #pragma once +#include "magic_enum.hpp" + namespace org::apache::nifi::minifi::azure { enum class EntityTracking { @@ -26,4 +28,30 @@ enum class EntityTracking { timestamps }; +enum class CredentialConfigurationStrategyOption { + FromProperties, + DefaultCredential, + ManagedIdentity, + WorkloadIdentity +}; + } // namespace org::apache::nifi::minifi::azure + +namespace magic_enum::customize { +using CredentialConfigurationStrategyOption = org::apache::nifi::minifi::azure::CredentialConfigurationStrategyOption; + +template <> +constexpr customize_t enum_name<CredentialConfigurationStrategyOption>(CredentialConfigurationStrategyOption value) noexcept { + switch (value) { + case CredentialConfigurationStrategyOption::FromProperties: + return "From Properties"; + case CredentialConfigurationStrategyOption::DefaultCredential: + return "Default Credential"; + case CredentialConfigurationStrategyOption::ManagedIdentity: + return "Managed Identity"; + case CredentialConfigurationStrategyOption::WorkloadIdentity: + return "Workload Identity"; + } + return invalid_tag; +} +} // namespace magic_enum::customize
