diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index fcc79de788..f6220f1d63 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -14,6 +14,9 @@ All notable changes to the **Prowler API** are documented in this file. ### Fixed - Scan with no resources will not trigger legacy code for findings metadata [(#8183)](https://github.com/prowler-cloud/prowler/pull/8183) +### Removed +- Validation of the provider's secret type during updates [(#8197)](https://github.com/prowler-cloud/prowler/pull/8197) + --- ## [v1.9.0] (Prowler v5.8.0) diff --git a/api/src/backend/api/specs/v1.yaml b/api/src/backend/api/specs/v1.yaml index 9a56a91f65..14cfdee033 100644 --- a/api/src/backend/api/specs/v1.yaml +++ b/api/src/backend/api/specs/v1.yaml @@ -9863,7 +9863,6 @@ components: * `static` - Key-value pairs * `role` - Role assumption * `service_account` - GCP Service Account Key - readOnly: true secret: oneOf: - type: object @@ -11621,7 +11620,6 @@ components: * `static` - Key-value pairs * `role` - Role assumption * `service_account` - GCP Service Account Key - readOnly: true secret: oneOf: - type: object diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 5dc7f2f6c6..cb727f290b 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -2038,6 +2038,104 @@ class TestProviderSecretViewSet: ) assert response.status_code == status.HTTP_400_BAD_REQUEST + def test_provider_secrets_partial_update_with_secret_type( + self, authenticated_client, provider_secret_fixture + ): + provider_secret, *_ = provider_secret_fixture + data = { + "data": { + "type": "provider-secrets", + "id": str(provider_secret.id), + "attributes": { + "name": "new_name", + "secret": { + "service_account_key": {}, + }, + "secret_type": "service_account", + }, + "relationships": { + "provider": { + "data": { + "type": "providers", + "id": str(provider_secret.provider.id), + } + } + }, + } + } + response = authenticated_client.patch( + reverse("providersecret-detail", kwargs={"pk": provider_secret.id}), + data=json.dumps(data), + content_type="application/vnd.api+json", + ) + assert response.status_code == status.HTTP_200_OK + provider_secret.refresh_from_db() + assert provider_secret.name == "new_name" + assert provider_secret.secret == {"service_account_key": {}} + + def test_provider_secrets_partial_update_with_invalid_secret_type( + self, authenticated_client, provider_secret_fixture + ): + provider_secret, *_ = provider_secret_fixture + data = { + "data": { + "type": "provider-secrets", + "id": str(provider_secret.id), + "attributes": { + "name": "new_name", + "secret": { + "service_account_key": {}, + }, + "secret_type": "static", + }, + "relationships": { + "provider": { + "data": { + "type": "providers", + "id": str(provider_secret.provider.id), + } + } + }, + } + } + response = authenticated_client.patch( + reverse("providersecret-detail", kwargs={"pk": provider_secret.id}), + data=json.dumps(data), + content_type="application/vnd.api+json", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + def test_provider_secrets_partial_update_without_secret_type_but_different( + self, authenticated_client, provider_secret_fixture + ): + provider_secret, *_ = provider_secret_fixture + data = { + "data": { + "type": "provider-secrets", + "id": str(provider_secret.id), + "attributes": { + "name": "new_name", + "secret": { + "service_account_key": {}, + }, + }, + "relationships": { + "provider": { + "data": { + "type": "providers", + "id": str(provider_secret.provider.id), + } + } + }, + } + } + response = authenticated_client.patch( + reverse("providersecret-detail", kwargs={"pk": provider_secret.id}), + data=json.dumps(data), + content_type="application/vnd.api+json", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + @pytest.mark.django_db class TestScanViewSet: diff --git a/api/src/backend/api/v1/serializers.py b/api/src/backend/api/v1/serializers.py index 09b1fd1bf0..82c11958cc 100644 --- a/api/src/backend/api/v1/serializers.py +++ b/api/src/backend/api/v1/serializers.py @@ -1315,12 +1315,13 @@ class ProviderSecretUpdateSerializer(BaseWriteProviderSecretSerializer): "inserted_at": {"read_only": True}, "updated_at": {"read_only": True}, "provider": {"read_only": True}, - "secret_type": {"read_only": True}, + "secret_type": {"required": False}, } def validate(self, attrs): provider = self.instance.provider - secret_type = self.instance.secret_type + # To allow updating a secret with the same type without making the `secret_type` mandatory + secret_type = attrs.get("secret_type") or self.instance.secret_type secret = attrs.get("secret") validated_attrs = super().validate(attrs) diff --git a/ui/CHANGELOG.md b/ui/CHANGELOG.md index addad45c29..c9a5e56a5e 100644 --- a/ui/CHANGELOG.md +++ b/ui/CHANGELOG.md @@ -13,6 +13,14 @@ All notable changes to the **Prowler UI** are documented in this file. - Upgrade to Next.js 14.2.30 and lock TypeScript to 5.5.4 for ESLint compatibility [(#8189)](https://github.com/prowler-cloud/prowler/pull/8189) ### 🐞 Fixed +### Removed + +--- + +## [v1.8.1] (Prowler 5.8.1) + +### Removed +- Validation of the provider's secret type during updates [(#8197)](https://github.com/prowler-cloud/prowler/pull/8197) --- diff --git a/ui/actions/providers/providers.ts b/ui/actions/providers/providers.ts index 922041d487..7a38b8875f 100644 --- a/ui/actions/providers/providers.ts +++ b/ui/actions/providers/providers.ts @@ -13,7 +13,6 @@ import { } from "@/lib"; import { buildSecretConfig, - buildUpdateSecretConfig, handleApiError, handleApiResponse, } from "@/lib/provider-credentials/build-crendentials"; @@ -194,8 +193,7 @@ export const updateCredentialsProvider = async ( ) as ProviderType; try { - const secret = buildUpdateSecretConfig(formData, providerType); - + const { secretType, secret } = buildSecretConfig(formData, providerType); const response = await fetch(url.toString(), { method: "PATCH", headers, @@ -203,7 +201,7 @@ export const updateCredentialsProvider = async ( data: { type: "provider-secrets", id: credentialsId, - attributes: { secret }, + attributes: { secret_type: secretType, secret }, }, }), }); diff --git a/ui/components/providers/credentials-update-info.tsx b/ui/components/providers/credentials-update-info.tsx index 1b32e2391f..2715a1ea6b 100644 --- a/ui/components/providers/credentials-update-info.tsx +++ b/ui/components/providers/credentials-update-info.tsx @@ -1,6 +1,5 @@ "use client"; -import { InfoIcon } from "@/components/icons"; import { SelectViaAWS } from "@/components/providers/workflow/forms/select-credentials-type/aws"; import { SelectViaGCP } from "@/components/providers/workflow/forms/select-credentials-type/gcp"; import { ProviderType } from "@/types/providers"; @@ -24,28 +23,5 @@ export const CredentialsUpdateInfo = ({ return null; }; - return ( -
-

- To update provider credentials,{" "} - - the same type that was originally configured must be used. - -

-
- -

- If the provider was configured with static credentials, updates must - also use static credentials. If it was configured with a role in AWS - (or service account in GCP),{" "} - updates must use the same type. -

-
-

- To switch from one type to another, the provider must be deleted and set - up again. -

- {renderSelectComponent()} -
- ); + return
{renderSelectComponent()}
; }; diff --git a/ui/lib/provider-credentials/build-crendentials.ts b/ui/lib/provider-credentials/build-crendentials.ts index 674d263386..791a209c8c 100644 --- a/ui/lib/provider-credentials/build-crendentials.ts +++ b/ui/lib/provider-credentials/build-crendentials.ts @@ -187,16 +187,6 @@ export const buildSecretConfig = ( return builder(); }; -// Helper function to build secret for update (reuses existing logic) -export const buildUpdateSecretConfig = ( - formData: FormData, - providerType: ProviderType, -) => { - // Reuse the same secret building logic as add - const { secret } = buildSecretConfig(formData, providerType); - return secret; -}; - // Helper function to handle API responses consistently export const handleApiResponse = async ( response: Response,