From b8ae37549049548b7bd39ca7c8c6ef61992b0018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20PEZ=C3=89?= Date: Wed, 16 Sep 2026 16:14:57 +0200 Subject: [PATCH 1/2] feat(stratus): add credential reference element on contract (#7850) --- stratus/stratus/contracts/__init__.py | 4 ++ stratus/test/test_contracts.py | 81 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/stratus/stratus/contracts/__init__.py b/stratus/stratus/contracts/__init__.py index 2f374d4b..4a828a3c 100644 --- a/stratus/stratus/contracts/__init__.py +++ b/stratus/stratus/contracts/__init__.py @@ -32,6 +32,7 @@ SupportedLanguage, prepare_contracts, ) +from pyoaev.credential.utils import build_single_referenced_credential_element from pyoaev.security_domain.types import SecurityDomains from stratus.contracts.platforms import ( # noqa: F401 (re-exported) @@ -140,6 +141,9 @@ def _make_contract( elements: List[ContractElement] = [ _credential_element(cred) for cred in platform.cred_fields ] + # Centralized credential-reference field: lets the OpenAEV inject form ask + # for a stored credential, filtered on the platform credential type. + elements.append(build_single_referenced_credential_element(platform.key)) elements.extend(extra_fields) elements.append(_expectations_element()) diff --git a/stratus/test/test_contracts.py b/stratus/test/test_contracts.py index 16b6f0ed..976c58d3 100644 --- a/stratus/test/test_contracts.py +++ b/stratus/test/test_contracts.py @@ -1,6 +1,9 @@ import json from unittest import TestCase +from pyoaev.contracts.contract_config import ContractFieldKey, ContractFieldType +from pyoaev.credential.types import CredentialType + from stratus.contracts import ( CONTRACT_REGISTRY, CONTRACT_TYPE, @@ -11,6 +14,19 @@ from stratus.contracts.platforms import PLATFORMS, PLATFORMS_BY_KEY from stratus.contracts.techniques import TECHNIQUES +CREDENTIAL_REFERENCE_KEY = ContractFieldKey.CredentialReference.value + +# entra-id and k8s have no dedicated platform credential type: a None value is +# intentional, the frontend then applies no credential filter. +EXPECTED_CREDENTIAL_TYPE_BY_PLATFORM = { + "aws": CredentialType.CLOUD_AWS.value, + "eks": CredentialType.CLOUD_AWS.value, + "azure": CredentialType.CLOUD_AZURE.value, + "entra-id": None, + "gcp": CredentialType.CLOUD_GCP.value, + "k8s": None, +} + class ContractsTest(TestCase): def setUp(self): @@ -75,3 +91,68 @@ def test_labels_are_platform_prefixed(self): self.assertTrue( contract["contract_labels"]["en"].startswith(platform.label + " - ") ) + + def _credential_reference_field(self, content): + fields = [f for f in content["fields"] if f["key"] == CREDENTIAL_REFERENCE_KEY] + self.assertEqual(len(fields), 1) + return fields[0] + + def test_every_contract_exposes_exactly_one_credential_reference(self): + for content in self.content_by_id.values(): + field = self._credential_reference_field(content) + self.assertEqual(field["type"], ContractFieldType.CredentialReference.value) + + def test_credential_reference_is_mandatory_and_single_valued(self): + for content in self.content_by_id.values(): + field = self._credential_reference_field(content) + self.assertTrue(field["mandatory"]) + self.assertFalse(field["multiple"]) + + def test_custom_contracts_credential_reference_type_matches_platform(self): + for platform in PLATFORMS: + content = self.content_by_id[platform.custom_contract_id] + field = self._credential_reference_field(content) + self.assertEqual( + field["credential_reference_type"], + EXPECTED_CREDENTIAL_TYPE_BY_PLATFORM[platform.key], + ) + + def test_technique_contracts_credential_reference_type_matches_platform(self): + for technique in TECHNIQUES: + content = self.content_by_id[technique_contract_id(technique.id)] + field = self._credential_reference_field(content) + self.assertEqual( + field["credential_reference_type"], + EXPECTED_CREDENTIAL_TYPE_BY_PLATFORM[technique.platform], + ) + + def test_every_platform_has_an_expected_credential_type(self): + self.assertEqual( + {p.key for p in PLATFORMS}, set(EXPECTED_CREDENTIAL_TYPE_BY_PLATFORM) + ) + + def test_credential_reference_does_not_collide_with_platform_cred_fields(self): + for platform in PLATFORMS: + cred_keys = {cred.key for cred in platform.cred_fields} + self.assertNotIn(CREDENTIAL_REFERENCE_KEY, cred_keys) + + def test_credential_reference_sits_after_credentials_before_expectations(self): + for technique in TECHNIQUES: + platform = PLATFORMS_BY_KEY[technique.platform] + content = self.content_by_id[technique_contract_id(technique.id)] + keys = [f["key"] for f in content["fields"]] + self.assertEqual( + keys.index(CREDENTIAL_REFERENCE_KEY), len(platform.cred_fields) + ) + self.assertLess( + keys.index(CREDENTIAL_REFERENCE_KEY), keys.index("expectations") + ) + + def test_custom_contracts_keep_credential_reference_before_technique_field(self): + for platform in PLATFORMS: + content = self.content_by_id[platform.custom_contract_id] + keys = [f["key"] for f in content["fields"]] + self.assertLess( + keys.index(CREDENTIAL_REFERENCE_KEY), + keys.index(CUSTOM_TECHNIQUE_FIELD_KEY), + ) From 204c1e3f7b5465b264c165a2002426d62847f897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20PEZ=C3=89?= Date: Fri, 2 Oct 2026 13:26:55 +0200 Subject: [PATCH 2/2] feat(credentials): fix tests avec pyoaev changes (#7850) --- stratus/test/test_contracts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratus/test/test_contracts.py b/stratus/test/test_contracts.py index 976c58d3..a0507d1f 100644 --- a/stratus/test/test_contracts.py +++ b/stratus/test/test_contracts.py @@ -105,7 +105,7 @@ def test_every_contract_exposes_exactly_one_credential_reference(self): def test_credential_reference_is_mandatory_and_single_valued(self): for content in self.content_by_id.values(): field = self._credential_reference_field(content) - self.assertTrue(field["mandatory"]) + self.assertFalse(field["mandatory"]) self.assertFalse(field["multiple"]) def test_custom_contracts_credential_reference_type_matches_platform(self):