mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat(api): support regionless OCI credentials (#11741)
This commit is contained in:
committed by
GitHub
parent
d7d4cc4849
commit
35b3ff2c8e
@@ -0,0 +1 @@
|
||||
OCI provider secrets no longer require `region`; legacy `region` input is accepted for backwards compatibility but ignored before storing or scanning
|
||||
@@ -3,7 +3,12 @@ from api.v1.serializer_utils.integrations import (
|
||||
JiraCredentialSerializer,
|
||||
S3ConfigSerializer,
|
||||
)
|
||||
from api.v1.serializers import ImageProviderSecret, KubernetesProviderSecret
|
||||
from api.v1.serializer_utils.providers import ProviderSecretField
|
||||
from api.v1.serializers import (
|
||||
ImageProviderSecret,
|
||||
KubernetesProviderSecret,
|
||||
OracleCloudProviderSecret,
|
||||
)
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
@@ -190,6 +195,64 @@ class TestImageProviderSecret:
|
||||
assert "non_field_errors" in serializer.errors
|
||||
|
||||
|
||||
class TestOracleCloudProviderSecret:
|
||||
def valid_secret(self, **overrides):
|
||||
secret = {
|
||||
"user": "ocid1.user.oc1..aaaaaaaexample",
|
||||
"fingerprint": "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99",
|
||||
"key_content": "fake-base64-key-content",
|
||||
"tenancy": "ocid1.tenancy.oc1..aaaaaaaexample",
|
||||
}
|
||||
secret.update(overrides)
|
||||
return secret
|
||||
|
||||
def test_accepts_regionless_secret(self):
|
||||
serializer = OracleCloudProviderSecret(data=self.valid_secret())
|
||||
|
||||
assert serializer.is_valid(), serializer.errors
|
||||
assert "region" not in serializer.validated_data
|
||||
|
||||
def test_accepts_and_ignores_region_field(self):
|
||||
secret = self.valid_secret(region="us-phoenix-1")
|
||||
serializer = OracleCloudProviderSecret(data=secret)
|
||||
|
||||
assert serializer.is_valid(), serializer.errors
|
||||
|
||||
assert "region" not in serializer.validated_data
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"legacy_field, legacy_value",
|
||||
[
|
||||
("region", None),
|
||||
("region", ""),
|
||||
("region", {"name": "us-ashburn-1"}),
|
||||
],
|
||||
)
|
||||
def test_accepts_and_ignores_any_legacy_region_value(
|
||||
self, legacy_field, legacy_value
|
||||
):
|
||||
serializer = OracleCloudProviderSecret(
|
||||
data=self.valid_secret(**{legacy_field: legacy_value})
|
||||
)
|
||||
|
||||
assert serializer.is_valid(), serializer.errors
|
||||
|
||||
assert legacy_field not in serializer.validated_data
|
||||
|
||||
|
||||
class TestProviderSecretFieldSchema:
|
||||
def test_oraclecloud_schema_includes_legacy_region_field(self):
|
||||
schema = ProviderSecretField._spectacular_annotation["field"]
|
||||
oraclecloud_schema = next(
|
||||
credential_schema
|
||||
for credential_schema in schema["oneOf"]
|
||||
if credential_schema["title"]
|
||||
== "Oracle Cloud Infrastructure (OCI) API Key Credentials"
|
||||
)
|
||||
|
||||
assert oraclecloud_schema["properties"]["region"]["deprecated"] is True
|
||||
|
||||
|
||||
class TestKubernetesProviderSecret:
|
||||
def test_valid_static_kubeconfig_is_accepted(self):
|
||||
kubeconfig_content = """
|
||||
|
||||
@@ -171,6 +171,53 @@ class TestInitializeProwlerProvider:
|
||||
key="value", mutelist_content={"key": "value"}
|
||||
)
|
||||
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
def test_initialize_oraclecloud_provider_removes_region_string(
|
||||
self, mock_return_prowler_provider
|
||||
):
|
||||
provider = MagicMock()
|
||||
provider.provider = Provider.ProviderChoices.ORACLECLOUD.value
|
||||
provider.secret.secret = {
|
||||
"user": "ocid1.user.oc1..fake",
|
||||
"fingerprint": "00:11:22:33:44:55:66:77",
|
||||
"key_content": "fake-base64-key-content",
|
||||
"tenancy": "ocid1.tenancy.oc1..fake",
|
||||
"region": "us-ashburn-1",
|
||||
}
|
||||
mock_return_prowler_provider.return_value = MagicMock()
|
||||
|
||||
initialize_prowler_provider(provider)
|
||||
|
||||
mock_return_prowler_provider.return_value.assert_called_once_with(
|
||||
user="ocid1.user.oc1..fake",
|
||||
fingerprint="00:11:22:33:44:55:66:77",
|
||||
key_content="fake-base64-key-content",
|
||||
tenancy="ocid1.tenancy.oc1..fake",
|
||||
)
|
||||
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
def test_initialize_oraclecloud_provider_without_region_omits_scan_filter(
|
||||
self, mock_return_prowler_provider
|
||||
):
|
||||
provider = MagicMock()
|
||||
provider.provider = Provider.ProviderChoices.ORACLECLOUD.value
|
||||
provider.secret.secret = {
|
||||
"user": "ocid1.user.oc1..fake",
|
||||
"fingerprint": "00:11:22:33:44:55:66:77",
|
||||
"key_content": "fake-base64-key-content",
|
||||
"tenancy": "ocid1.tenancy.oc1..fake",
|
||||
}
|
||||
mock_return_prowler_provider.return_value = MagicMock()
|
||||
|
||||
initialize_prowler_provider(provider)
|
||||
|
||||
mock_return_prowler_provider.return_value.assert_called_once_with(
|
||||
user="ocid1.user.oc1..fake",
|
||||
fingerprint="00:11:22:33:44:55:66:77",
|
||||
key_content="fake-base64-key-content",
|
||||
tenancy="ocid1.tenancy.oc1..fake",
|
||||
)
|
||||
|
||||
|
||||
class TestProwlerProviderConnectionTest:
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
@@ -185,6 +232,37 @@ class TestProwlerProviderConnectionTest:
|
||||
key="value", provider_id="1234567890", raise_on_exception=False
|
||||
)
|
||||
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
def test_oraclecloud_connection_test_uses_direct_credentials_without_region(
|
||||
self, mock_return_prowler_provider
|
||||
):
|
||||
provider = MagicMock()
|
||||
provider.uid = "ocid1.tenancy.oc1..aaaaaaaexample"
|
||||
provider.provider = Provider.ProviderChoices.ORACLECLOUD.value
|
||||
provider.secret.secret = {
|
||||
"user": "ocid1.user.oc1..aaaaaaaexample",
|
||||
"fingerprint": "00:11:22:33:44:55:66:77",
|
||||
"key_content": "fake-base64-key-content",
|
||||
"tenancy": "ocid1.tenancy.oc1..aaaaaaaexample",
|
||||
}
|
||||
mock_return_prowler_provider.return_value = MagicMock()
|
||||
|
||||
prowler_provider_connection_test(provider)
|
||||
|
||||
mock_return_prowler_provider.return_value.test_connection.assert_called_once_with(
|
||||
user="ocid1.user.oc1..aaaaaaaexample",
|
||||
fingerprint="00:11:22:33:44:55:66:77",
|
||||
key_content="fake-base64-key-content",
|
||||
tenancy="ocid1.tenancy.oc1..aaaaaaaexample",
|
||||
region=getattr(
|
||||
OraclecloudProvider,
|
||||
"_bootstrap_region",
|
||||
OraclecloudProvider._home_region,
|
||||
),
|
||||
provider_id="ocid1.tenancy.oc1..aaaaaaaexample",
|
||||
raise_on_exception=False,
|
||||
)
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("api.utils.return_prowler_provider")
|
||||
def test_prowler_provider_connection_test_without_secret(
|
||||
@@ -356,7 +434,7 @@ class TestGetProwlerProviderKwargs:
|
||||
expected_result = {**secret_dict, **expected_extra_kwargs}
|
||||
assert result == expected_result
|
||||
|
||||
def test_get_prowler_provider_kwargs_oraclecloud_converts_region_string_to_set(
|
||||
def test_get_prowler_provider_kwargs_oraclecloud_removes_region(
|
||||
self,
|
||||
):
|
||||
secret_dict = {
|
||||
@@ -377,8 +455,13 @@ class TestGetProwlerProviderKwargs:
|
||||
|
||||
result = get_prowler_provider_kwargs(provider)
|
||||
|
||||
expected_result = {**secret_dict, "region": {"us-ashburn-1"}}
|
||||
assert result == expected_result
|
||||
assert result == {
|
||||
"user": "ocid1.user.oc1..fake",
|
||||
"fingerprint": "00:11:22:33:44:55:66:77",
|
||||
"key_content": "-----BEGIN PRIVATE KEY-----\nfake\n-----END PRIVATE KEY-----",
|
||||
"tenancy": "ocid1.tenancy.oc1..fake",
|
||||
"pass_phrase": "fake-passphrase",
|
||||
}
|
||||
|
||||
def test_get_prowler_provider_kwargs_with_mutelist(self):
|
||||
provider_uid = "provider_uid"
|
||||
|
||||
@@ -2917,6 +2917,48 @@ class TestProviderGroupViewSet:
|
||||
|
||||
@pytest.mark.django_db
|
||||
class TestProviderSecretViewSet:
|
||||
@staticmethod
|
||||
def _oraclecloud_secret(**overrides):
|
||||
secret = {
|
||||
"user": "ocid1.user.oc1..aaaaaaaakldibrbov4ubh25aqdeiroklxjngwka7u6w7no3glmdq3n5sxtkq",
|
||||
"fingerprint": "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99",
|
||||
"key_content": "test-key-content",
|
||||
"tenancy": "ocid1.tenancy.oc1..aaaaaaaa3dwoazoox4q7wrvriywpokp5grlhgnkwtyt6dmwyou7no6mdmzda",
|
||||
}
|
||||
secret.update(overrides)
|
||||
return secret
|
||||
|
||||
def _create_oraclecloud_secret(
|
||||
self,
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
secret,
|
||||
name="OCI Secret",
|
||||
):
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"attributes": {
|
||||
"name": name,
|
||||
"secret_type": ProviderSecret.TypeChoices.STATIC,
|
||||
"secret": secret,
|
||||
},
|
||||
"relationships": {
|
||||
"provider": {
|
||||
"data": {
|
||||
"type": "providers",
|
||||
"id": str(oraclecloud_provider.id),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
return authenticated_client.post(
|
||||
reverse("providersecret-list"),
|
||||
data=json.dumps(data),
|
||||
content_type="application/vnd.api+json",
|
||||
)
|
||||
|
||||
def test_provider_secrets_list(self, authenticated_client, provider_secret_fixture):
|
||||
response = authenticated_client.get(reverse("providersecret-list"))
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
@@ -3076,7 +3118,6 @@ current-context: test-context
|
||||
"fingerprint": "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99",
|
||||
"key_content": "-----BEGIN RSA PRIVATE KEY-----\ntest-key-content\n-----END RSA PRIVATE KEY-----",
|
||||
"tenancy": "ocid1.tenancy.oc1..aaaaaaaa3dwoazoox4q7wrvriywpokp5grlhgnkwtyt6dmwyou7no6mdmzda",
|
||||
"region": "us-ashburn-1",
|
||||
},
|
||||
),
|
||||
# OCI with API key credentials (with key_file)
|
||||
@@ -3088,7 +3129,6 @@ current-context: test-context
|
||||
"fingerprint": "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99",
|
||||
"key_file": "/path/to/oci_api_key.pem",
|
||||
"tenancy": "ocid1.tenancy.oc1..aaaaaaaa3dwoazoox4q7wrvriywpokp5grlhgnkwtyt6dmwyou7no6mdmzda",
|
||||
"region": "us-ashburn-1",
|
||||
},
|
||||
),
|
||||
# OCI with API key credentials (with passphrase)
|
||||
@@ -3100,7 +3140,6 @@ current-context: test-context
|
||||
"fingerprint": "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99",
|
||||
"key_content": "-----BEGIN RSA PRIVATE KEY-----\ntest-encrypted-key\n-----END RSA PRIVATE KEY-----",
|
||||
"tenancy": "ocid1.tenancy.oc1..aaaaaaaa3dwoazoox4q7wrvriywpokp5grlhgnkwtyt6dmwyou7no6mdmzda",
|
||||
"region": "us-ashburn-1",
|
||||
"pass_phrase": "my-secure-passphrase",
|
||||
},
|
||||
),
|
||||
@@ -3258,6 +3297,103 @@ current-context: test-context
|
||||
== data["data"]["relationships"]["provider"]["data"]["id"]
|
||||
)
|
||||
|
||||
def test_provider_secrets_create_oraclecloud_without_region_stores_no_region(
|
||||
self,
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
):
|
||||
response = self._create_oraclecloud_secret(
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
self._oraclecloud_secret(),
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
provider_secret = ProviderSecret.objects.get()
|
||||
assert "region" not in provider_secret.secret
|
||||
|
||||
def test_provider_secrets_create_oraclecloud_accepts_and_ignores_region(
|
||||
self,
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
):
|
||||
response = self._create_oraclecloud_secret(
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
self._oraclecloud_secret(
|
||||
key_content=" test-key-content ", region=" us-ashburn-1 "
|
||||
),
|
||||
)
|
||||
|
||||
assert response.status_code == status.HTTP_201_CREATED
|
||||
provider_secret = ProviderSecret.objects.get()
|
||||
assert provider_secret.secret["key_content"] == "test-key-content"
|
||||
assert "region" not in provider_secret.secret
|
||||
|
||||
def test_provider_secrets_update_oraclecloud_without_region_stores_no_region(
|
||||
self,
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
):
|
||||
create_response = self._create_oraclecloud_secret(
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
self._oraclecloud_secret(),
|
||||
)
|
||||
provider_secret = ProviderSecret.objects.get(
|
||||
id=create_response.json()["data"]["id"]
|
||||
)
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"id": str(provider_secret.id),
|
||||
"attributes": {"secret": self._oraclecloud_secret()},
|
||||
}
|
||||
}
|
||||
|
||||
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 "region" not in provider_secret.secret
|
||||
|
||||
def test_provider_secrets_update_oraclecloud_accepts_and_ignores_region(
|
||||
self,
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
):
|
||||
create_response = self._create_oraclecloud_secret(
|
||||
authenticated_client,
|
||||
oraclecloud_provider,
|
||||
self._oraclecloud_secret(),
|
||||
)
|
||||
provider_secret = ProviderSecret.objects.get(
|
||||
id=create_response.json()["data"]["id"]
|
||||
)
|
||||
data = {
|
||||
"data": {
|
||||
"type": "provider-secrets",
|
||||
"id": str(provider_secret.id),
|
||||
"attributes": {
|
||||
"secret": self._oraclecloud_secret(region=" us-ashburn-1 ")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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 "region" not in provider_secret.secret
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"attributes, error_code, error_pointer",
|
||||
(
|
||||
|
||||
@@ -252,12 +252,6 @@ def get_prowler_provider_kwargs(
|
||||
**prowler_provider_kwargs,
|
||||
"filter_accounts": [provider.uid],
|
||||
}
|
||||
elif provider.provider == Provider.ProviderChoices.ORACLECLOUD.value:
|
||||
if isinstance(prowler_provider_kwargs.get("region"), str):
|
||||
prowler_provider_kwargs = {
|
||||
**prowler_provider_kwargs,
|
||||
"region": {prowler_provider_kwargs["region"]},
|
||||
}
|
||||
elif provider.provider == Provider.ProviderChoices.OPENSTACK.value:
|
||||
# clouds_yaml_content, clouds_yaml_cloud and provider_id are validated
|
||||
# in the provider itself, so it's not needed here.
|
||||
@@ -288,6 +282,11 @@ def get_prowler_provider_kwargs(
|
||||
**{k: v for k, v in prowler_provider_kwargs.items() if v},
|
||||
}
|
||||
|
||||
elif provider.provider == Provider.ProviderChoices.ORACLECLOUD.value:
|
||||
prowler_provider_kwargs = _normalize_oraclecloud_provider_kwargs(
|
||||
prowler_provider_kwargs
|
||||
)
|
||||
|
||||
if mutelist_processor:
|
||||
mutelist_content = mutelist_processor.configuration.get("Mutelist", {})
|
||||
# IaC and Image providers don't support mutelist (both use Trivy's built-in logic)
|
||||
@@ -300,6 +299,40 @@ def get_prowler_provider_kwargs(
|
||||
return prowler_provider_kwargs
|
||||
|
||||
|
||||
def _normalize_oraclecloud_provider_kwargs(secret: dict) -> dict:
|
||||
"""Normalize external OCI secret fields into SDK provider kwargs."""
|
||||
prowler_provider_kwargs = secret.copy()
|
||||
prowler_provider_kwargs.pop("region", None)
|
||||
|
||||
return prowler_provider_kwargs
|
||||
|
||||
|
||||
def _normalize_oraclecloud_connection_test_kwargs(secret: dict) -> dict:
|
||||
"""Normalize external OCI secret fields into test_connection kwargs."""
|
||||
from prowler.providers.oraclecloud.oraclecloud_provider import OraclecloudProvider
|
||||
|
||||
prowler_provider_kwargs = secret.copy()
|
||||
prowler_provider_kwargs.pop("region", None)
|
||||
|
||||
if (
|
||||
prowler_provider_kwargs.get("user")
|
||||
and prowler_provider_kwargs.get("fingerprint")
|
||||
and prowler_provider_kwargs.get("tenancy")
|
||||
and (
|
||||
prowler_provider_kwargs.get("key_content")
|
||||
or prowler_provider_kwargs.get("key_file")
|
||||
)
|
||||
):
|
||||
# Connection validation needs one OCI endpoint, but scans remain unfiltered.
|
||||
prowler_provider_kwargs["region"] = getattr(
|
||||
OraclecloudProvider,
|
||||
"_bootstrap_region",
|
||||
OraclecloudProvider._home_region,
|
||||
)
|
||||
|
||||
return prowler_provider_kwargs
|
||||
|
||||
|
||||
def initialize_prowler_provider(
|
||||
provider: Provider,
|
||||
mutelist_processor: Processor | None = None,
|
||||
@@ -402,6 +435,15 @@ def prowler_provider_connection_test(provider: Provider) -> Connection:
|
||||
if prowler_provider_kwargs.get("registry_token"):
|
||||
image_kwargs["registry_token"] = prowler_provider_kwargs["registry_token"]
|
||||
return prowler_provider.test_connection(**image_kwargs)
|
||||
elif provider.provider == Provider.ProviderChoices.ORACLECLOUD.value:
|
||||
oraclecloud_kwargs = _normalize_oraclecloud_connection_test_kwargs(
|
||||
prowler_provider_kwargs
|
||||
)
|
||||
return prowler_provider.test_connection(
|
||||
**oraclecloud_kwargs,
|
||||
provider_id=provider.uid,
|
||||
raise_on_exception=False,
|
||||
)
|
||||
else:
|
||||
return prowler_provider.test_connection(
|
||||
**prowler_provider_kwargs,
|
||||
|
||||
@@ -295,16 +295,21 @@ from rest_framework_json_api import serializers
|
||||
"type": "string",
|
||||
"description": "The OCID of the tenancy.",
|
||||
},
|
||||
"region": {
|
||||
"type": "string",
|
||||
"description": "The OCI region identifier (e.g., us-ashburn-1, us-phoenix-1).",
|
||||
},
|
||||
"pass_phrase": {
|
||||
"type": "string",
|
||||
"description": "The passphrase for the private key, if encrypted.",
|
||||
},
|
||||
"region": {
|
||||
"type": "string",
|
||||
"deprecated": True,
|
||||
"description": "Legacy OCI region field accepted for backwards compatibility but ignored; OCI scans all regions.",
|
||||
},
|
||||
},
|
||||
"required": ["user", "fingerprint", "tenancy", "region"],
|
||||
"required": ["user", "fingerprint", "tenancy"],
|
||||
"anyOf": [
|
||||
{"required": ["key_file"]},
|
||||
{"required": ["key_content"]},
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
|
||||
@@ -1672,6 +1672,7 @@ class BaseWriteProviderSecretSerializer(BaseWriteSerializer):
|
||||
validation_error.detail[f"secret/{key}"] = value
|
||||
del validation_error.detail[key]
|
||||
raise validation_error
|
||||
return serializer.validated_data
|
||||
|
||||
|
||||
class AwsProviderSecret(serializers.Serializer):
|
||||
@@ -1813,14 +1814,32 @@ class IacProviderSecret(serializers.Serializer):
|
||||
resource_name = "provider-secrets"
|
||||
|
||||
|
||||
class LegacyOCIRegionField(serializers.Field):
|
||||
def to_internal_value(self, data):
|
||||
return data
|
||||
|
||||
def to_representation(self, value):
|
||||
return value
|
||||
|
||||
|
||||
class OracleCloudProviderSecret(serializers.Serializer):
|
||||
user = serializers.CharField()
|
||||
fingerprint = serializers.CharField()
|
||||
key_file = serializers.CharField(required=False)
|
||||
key_content = serializers.CharField(required=False)
|
||||
tenancy = serializers.CharField()
|
||||
region = serializers.CharField()
|
||||
pass_phrase = serializers.CharField(required=False)
|
||||
region = LegacyOCIRegionField(required=False, allow_null=True)
|
||||
|
||||
def validate(self, attrs):
|
||||
attrs.pop("region", None)
|
||||
|
||||
if "key_file" not in attrs and "key_content" not in attrs:
|
||||
raise serializers.ValidationError(
|
||||
{"key_file": "Either key_file or key_content must be provided."}
|
||||
)
|
||||
|
||||
return attrs
|
||||
|
||||
class Meta:
|
||||
resource_name = "provider-secrets"
|
||||
@@ -1965,7 +1984,11 @@ class ProviderSecretCreateSerializer(RLSSerializer, BaseWriteProviderSecretSeria
|
||||
secret = attrs.get("secret")
|
||||
|
||||
validated_attrs = super().validate(attrs)
|
||||
self.validate_secret_based_on_provider(provider.provider, secret_type, secret)
|
||||
validated_secret = self.validate_secret_based_on_provider(
|
||||
provider.provider, secret_type, secret
|
||||
)
|
||||
if provider.provider == Provider.ProviderChoices.ORACLECLOUD.value:
|
||||
validated_attrs["secret"] = validated_secret
|
||||
return validated_attrs
|
||||
|
||||
|
||||
@@ -1997,7 +2020,11 @@ class ProviderSecretUpdateSerializer(BaseWriteProviderSecretSerializer):
|
||||
secret = attrs.get("secret")
|
||||
|
||||
validated_attrs = super().validate(attrs)
|
||||
self.validate_secret_based_on_provider(provider.provider, secret_type, secret)
|
||||
validated_secret = self.validate_secret_based_on_provider(
|
||||
provider.provider, secret_type, secret
|
||||
)
|
||||
if provider.provider == Provider.ProviderChoices.ORACLECLOUD.value:
|
||||
validated_attrs["secret"] = validated_secret
|
||||
return validated_attrs
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
OCI provider E2E tests no longer require or submit a region when adding or updating credentials
|
||||
-10
@@ -48,16 +48,6 @@ export const OracleCloudCredentialsForm = ({
|
||||
variant="bordered"
|
||||
isRequired
|
||||
/>
|
||||
<WizardInputField
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_REGION}
|
||||
type="text"
|
||||
label="Region"
|
||||
labelPlacement="inside"
|
||||
placeholder="e.g. us-ashburn-1"
|
||||
variant="bordered"
|
||||
isRequired
|
||||
/>
|
||||
<WizardTextareaField
|
||||
control={control}
|
||||
name={ProviderCredentialFields.OCI_KEY_CONTENT}
|
||||
|
||||
@@ -180,7 +180,6 @@ export const useCredentialsForm = ({
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: "",
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: "",
|
||||
[ProviderCredentialFields.OCI_TENANCY]: providerUid || "",
|
||||
[ProviderCredentialFields.OCI_REGION]: "",
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: "",
|
||||
};
|
||||
case "mongodbatlas":
|
||||
|
||||
@@ -389,10 +389,6 @@ export const buildOracleCloudSecret = (
|
||||
[ProviderCredentialFields.OCI_TENANCY]:
|
||||
providerUid ||
|
||||
getFormValue(formData, ProviderCredentialFields.OCI_TENANCY),
|
||||
[ProviderCredentialFields.OCI_REGION]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_REGION,
|
||||
),
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: getFormValue(
|
||||
formData,
|
||||
ProviderCredentialFields.OCI_PASS_PHRASE,
|
||||
|
||||
@@ -224,7 +224,6 @@ export interface OCIProviderCredential {
|
||||
userId?: string;
|
||||
fingerprint?: string;
|
||||
keyContent?: string;
|
||||
region?: string;
|
||||
}
|
||||
|
||||
// AlibabaCloud credential options
|
||||
@@ -366,7 +365,6 @@ export class ProvidersPage extends BasePage {
|
||||
readonly ociUserIdInput: Locator;
|
||||
readonly ociFingerprintInput: Locator;
|
||||
readonly ociKeyContentInput: Locator;
|
||||
readonly ociRegionInput: Locator;
|
||||
|
||||
// AlibabaCloud provider form elements
|
||||
readonly alibabacloudAccountIdInput: Locator;
|
||||
@@ -510,7 +508,6 @@ export class ProvidersPage extends BasePage {
|
||||
this.ociKeyContentInput = page.getByRole("textbox", {
|
||||
name: /Private Key Content/i,
|
||||
});
|
||||
this.ociRegionInput = page.getByRole("textbox", { name: /Region/i });
|
||||
|
||||
// AlibabaCloud provider form inputs
|
||||
this.alibabacloudAccountIdInput = page.getByRole("textbox", {
|
||||
@@ -1300,9 +1297,6 @@ export class ProvidersPage extends BasePage {
|
||||
if (credentials.keyContent) {
|
||||
await this.ociKeyContentInput.fill(credentials.keyContent);
|
||||
}
|
||||
if (credentials.region) {
|
||||
await this.ociRegionInput.fill(credentials.region);
|
||||
}
|
||||
}
|
||||
|
||||
async verifyOCICredentialsPageLoaded(): Promise<void> {
|
||||
@@ -1313,7 +1307,6 @@ export class ProvidersPage extends BasePage {
|
||||
await expect(this.ociUserIdInput).toBeVisible();
|
||||
await expect(this.ociFingerprintInput).toBeVisible();
|
||||
await expect(this.ociKeyContentInput).toBeVisible();
|
||||
await expect(this.ociRegionInput).toBeVisible();
|
||||
}
|
||||
|
||||
async verifyOCIUpdateCredentialsPageLoaded(): Promise<void> {
|
||||
@@ -1324,7 +1317,6 @@ export class ProvidersPage extends BasePage {
|
||||
await expect(this.ociUserIdInput).toBeVisible();
|
||||
await expect(this.ociFingerprintInput).toBeVisible();
|
||||
await expect(this.ociKeyContentInput).toBeVisible();
|
||||
await expect(this.ociRegionInput).toBeVisible();
|
||||
}
|
||||
|
||||
async selectAlibabaCloudProvider(): Promise<void> {
|
||||
|
||||
@@ -667,7 +667,7 @@
|
||||
**Preconditions:**
|
||||
|
||||
- Admin user authentication required (admin.auth.setup setup)
|
||||
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT, E2E_OCI_REGION
|
||||
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT
|
||||
- Remove any existing provider with the same Tenancy ID before starting the test
|
||||
- This test must be run serially and never in parallel with other tests, as it requires the Tenancy ID not to be already registered beforehand.
|
||||
|
||||
@@ -678,7 +678,7 @@
|
||||
3. Select OCI provider type
|
||||
4. Fill provider details (tenancy ID and alias)
|
||||
5. Verify OCI credentials page is loaded
|
||||
6. Fill OCI credentials (user ID, fingerprint, key content, region)
|
||||
6. Fill OCI credentials (user ID, fingerprint, key content)
|
||||
7. Confirm provider connection without launching a scan
|
||||
8. Verify return to Providers page
|
||||
9. Verify provider exists in Providers table
|
||||
@@ -696,7 +696,7 @@
|
||||
- Connect account page displays OCI option
|
||||
- Provider details form accepts tenancy ID and alias
|
||||
- OCI credentials page loads
|
||||
- Credentials form accepts all required fields (user ID, fingerprint, key content, region)
|
||||
- Credentials form accepts all required fields (user ID, fingerprint, key content)
|
||||
- Launch step appears
|
||||
- Successful return to Providers page after closing the launch step
|
||||
- Provider exists in Providers table (verified by tenancy ID)
|
||||
@@ -726,7 +726,7 @@
|
||||
**Preconditions:**
|
||||
|
||||
- Admin user authentication required (admin.auth.setup setup)
|
||||
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT, E2E_OCI_REGION
|
||||
- Environment variables configured: E2E_OCI_TENANCY_ID, E2E_OCI_USER_ID, E2E_OCI_FINGERPRINT, E2E_OCI_KEY_CONTENT
|
||||
- An OCI provider with the specified Tenancy ID must already exist (run PROVIDER-E2E-012 first)
|
||||
- This test must be run serially and never in parallel with other tests
|
||||
|
||||
@@ -738,7 +738,7 @@
|
||||
4. Click "Update Credentials" option
|
||||
5. Verify update credentials page is loaded
|
||||
6. Verify OCI credentials form fields are visible (confirms providerUid is loaded)
|
||||
7. Fill OCI credentials (user ID, fingerprint, key content, region)
|
||||
7. Fill OCI credentials (user ID, fingerprint, key content)
|
||||
8. Click Next to submit
|
||||
9. Verify successful navigation to test connection page
|
||||
|
||||
@@ -756,7 +756,7 @@
|
||||
- OCI provider row is visible in providers table
|
||||
- Row actions dropdown opens and displays "Update Credentials" option
|
||||
- Update credentials page URL contains correct parameters
|
||||
- OCI credentials form displays all fields (tenancy ID, user ID, fingerprint, key content, region)
|
||||
- OCI credentials form displays all required fields (tenancy ID, user ID, fingerprint, key content)
|
||||
- Form submission succeeds (no silent failures due to missing provider UID)
|
||||
- Successful redirect to test connection page
|
||||
|
||||
|
||||
@@ -1029,12 +1029,11 @@ test.describe("Add Provider", () => {
|
||||
const userId = process.env.E2E_OCI_USER_ID ?? "";
|
||||
const fingerprint = process.env.E2E_OCI_FINGERPRINT ?? "";
|
||||
const keyContent = process.env.E2E_OCI_KEY_CONTENT ?? "";
|
||||
const region = process.env.E2E_OCI_REGION ?? "";
|
||||
|
||||
// Setup before each test
|
||||
test.beforeEach(async ({ page }) => {
|
||||
test.skip(
|
||||
!tenancyId || !userId || !fingerprint || !keyContent || !region,
|
||||
!tenancyId || !userId || !fingerprint || !keyContent,
|
||||
"OCI E2E env vars are not set",
|
||||
);
|
||||
providersPage = new ProvidersPage(page);
|
||||
@@ -1071,7 +1070,6 @@ test.describe("Add Provider", () => {
|
||||
userId: userId,
|
||||
fingerprint: fingerprint,
|
||||
keyContent: keyContent,
|
||||
region: region,
|
||||
};
|
||||
|
||||
// Navigate to providers page
|
||||
@@ -1516,12 +1514,11 @@ test.describe("Update Provider Credentials", () => {
|
||||
const userId = process.env.E2E_OCI_USER_ID ?? "";
|
||||
const fingerprint = process.env.E2E_OCI_FINGERPRINT ?? "";
|
||||
const keyContent = process.env.E2E_OCI_KEY_CONTENT ?? "";
|
||||
const region = process.env.E2E_OCI_REGION ?? "";
|
||||
|
||||
// Setup before each test
|
||||
test.beforeEach(async ({ page }) => {
|
||||
test.skip(
|
||||
!tenancyId || !userId || !fingerprint || !keyContent || !region,
|
||||
!tenancyId || !userId || !fingerprint || !keyContent,
|
||||
"OCI E2E env vars are not set",
|
||||
);
|
||||
providersPage = new ProvidersPage(page);
|
||||
@@ -1543,7 +1540,6 @@ test.describe("Update Provider Credentials", () => {
|
||||
userId: userId,
|
||||
fingerprint: fingerprint,
|
||||
keyContent: keyContent,
|
||||
region: region,
|
||||
};
|
||||
|
||||
// Navigate to providers page
|
||||
|
||||
@@ -280,7 +280,6 @@ export type OCICredentials = {
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: string;
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]: string;
|
||||
[ProviderCredentialFields.OCI_TENANCY]: string;
|
||||
[ProviderCredentialFields.OCI_REGION]: string;
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]?: string;
|
||||
[ProviderCredentialFields.PROVIDER_ID]: string;
|
||||
};
|
||||
|
||||
Vendored
-1
@@ -134,7 +134,6 @@ declare global {
|
||||
E2E_OCI_USER_ID?: string;
|
||||
E2E_OCI_FINGERPRINT?: string;
|
||||
E2E_OCI_KEY_CONTENT?: string;
|
||||
E2E_OCI_REGION?: string;
|
||||
|
||||
// E2E Alibaba Cloud
|
||||
E2E_ALIBABACLOUD_ACCOUNT_ID?: string;
|
||||
|
||||
@@ -220,3 +220,23 @@ users:
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addCredentialsFormSchema - oraclecloud", () => {
|
||||
const BASE_OCI_VALUES = {
|
||||
[ProviderCredentialFields.PROVIDER_ID]: "provider-oci-1",
|
||||
[ProviderCredentialFields.PROVIDER_TYPE]: "oraclecloud",
|
||||
[ProviderCredentialFields.OCI_USER]: "ocid1.user.oc1..example",
|
||||
[ProviderCredentialFields.OCI_FINGERPRINT]: "aa:bb:cc:dd",
|
||||
[ProviderCredentialFields.OCI_KEY_CONTENT]:
|
||||
"-----BEGIN PRIVATE KEY-----\nMIIEvQ...\n-----END PRIVATE KEY-----",
|
||||
[ProviderCredentialFields.OCI_TENANCY]: "ocid1.tenancy.oc1..example",
|
||||
} as const;
|
||||
|
||||
it("accepts OCI API key credentials without region", () => {
|
||||
const schema = addCredentialsFormSchema("oraclecloud");
|
||||
|
||||
const result = schema.safeParse(BASE_OCI_VALUES);
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -288,9 +288,6 @@ export const addCredentialsFormSchema = (
|
||||
[ProviderCredentialFields.OCI_TENANCY]: z
|
||||
.string()
|
||||
.min(1, "Tenancy OCID is required"),
|
||||
[ProviderCredentialFields.OCI_REGION]: z
|
||||
.string()
|
||||
.min(1, "Region is required"),
|
||||
[ProviderCredentialFields.OCI_PASS_PHRASE]: z
|
||||
.union([z.string(), z.literal("")])
|
||||
.optional(),
|
||||
|
||||
Reference in New Issue
Block a user