Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion xtest/abac.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import enum

from pydantic import BaseModel, ConfigDict, Field
from pydantic import BaseModel, ConfigDict, Field, field_validator


class BaseModelIgnoreExtra(BaseModel):
Expand Down Expand Up @@ -53,6 +53,16 @@ class Attribute(BaseModelIgnoreExtra):
fqn: str | None
active: BoolValue | None = None
metadata: Metadata | None = None
allow_traversal: BoolValue | None = None

@field_validator("allow_traversal", mode="before")
@classmethod
def empty_allow_traversal_to_false(cls, value: object) -> object:
# Some policy service versions return `{}` for unset allow_traversal.
# Normalize that shape to an explicit false BoolValue payload.
if isinstance(value, dict) and len(value) == 0:
return {"value": False}
return value

@property
def value_fqns(self) -> list[str]:
Expand Down
36 changes: 36 additions & 0 deletions xtest/fixtures/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,42 @@ def one_attribute_ns_kas_grant(
return anyof


# Attribute definition with key mapping only (missing value FQN)
@pytest.fixture(scope="module")
def attribute_missing_value_key_mapping(
otdfctl: OpentdfCommandLineTool,
kas_entry_gamma: abac.KasEntry,
temporary_namespace: abac.Namespace,
root_key: str,
) -> tuple[str, str]:
"""Attribute with attribute-level managed key mapping and a missing value FQN."""
pfs = tdfs.PlatformFeatureSet()
if "key_management" not in pfs.features:
pytest.skip("Key management not supported by platform")

attr = otdfctl.attribute_create(
temporary_namespace,
"missingval",
abac.AttributeRule.ANY_OF,
["present"],
allow_traversal=True,
)
assert attr.fqn, "Attribute FQN is missing"

kas_key = otdfctl.kas_registry_create_key(
kas_entry_gamma,
key_id="missing-value-def",
mode="local",
algorithm="rsa:2048",
wrapping_key=root_key,
wrapping_key_id="root",
)
otdfctl.key_assign_attr(kas_key, attr)

missing_value_fqn = f"{attr.fqn}/value/missing"
return missing_value_fqn, kas_key.key.key_id


# Mixed grant scenarios (namespace + value)
@pytest.fixture(scope="module")
def ns_and_value_kas_grants_or(
Expand Down
9 changes: 8 additions & 1 deletion xtest/otdfctl.py
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,12 @@ def namespace_create(self, name: str) -> Namespace:
return Namespace.model_validate_json(out)

def attribute_create(
self, namespace: str | Namespace, name: str, t: AttributeRule, values: list[str]
self,
namespace: str | Namespace,
name: str,
t: AttributeRule,
values: list[str],
allow_traversal: bool | None = None,
) -> Attribute:
cmd = self.otdfctl + "policy attributes create".split()

Expand All @@ -651,6 +656,8 @@ def attribute_create(
]
if values:
cmd += [f"--value={','.join(values)}"]
if allow_traversal is not None:
cmd += [f"--allow-traversal={str(allow_traversal).lower()}"]
logger.info(f"attr-create [{' '.join(cmd)}]")
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = process.communicate()
Expand Down
6 changes: 6 additions & 0 deletions xtest/sdk/go/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ if [ "$1" == "supports" ]; then
"${cmd[@]}" --version --json | jq -re .sdk_version | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 > 10) || ($1 == 0 && $2 == 10 && $3 >= 0)) exit 0; else exit 1; }'
exit $?
;;
attribute_traversal)
# Attribute traversal support from go-sdk version >= 0.12.0
set -o pipefail
"${cmd[@]}" --version --json | jq -re .sdk_version | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 >= 12)) exit 0; else exit 1; }'
exit $?
;;
assertions | assertion_verification)
"${cmd[@]}" help decrypt | grep with-assertion-verification-keys
exit $?
Expand Down
5 changes: 5 additions & 0 deletions xtest/sdk/java/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ if [ "$1" == "supports" ]; then
java -jar "$SCRIPT_DIR"/cmdline.jar help encrypt | grep with-target-mode
exit $?
;;
attribute_traversal)
echo "attribute_traversal not supported"
exit 1
;;

mechanism-rsa-4096 | mechanism-ec-curves-384-521)
# rsa4096 support in >= 0.13.0
set -o pipefail
Expand Down
6 changes: 6 additions & 0 deletions xtest/sdk/js/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@
npx $CTL --version | jq -re '.["@opentdf/sdk"]' | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 >= 6)) exit 0; else exit 1; }'
exit $?
;;
attribute_traversal)
# Attribute traversal support from web-sdk version >= 0.9.0
set -o pipefail
npx $CTL --version | jq -re '.["@opentdf/sdk"]' | awk -F. '{ if ($1 > 0 || ($1 == 0 && $2 >= 9)) exit 0; else exit 1; }'

Check warning on line 91 in xtest/sdk/js/cli.sh

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of using the literal '.[\"@opentdf/sdk\"]' 4 times.

See more on https://sonarcloud.io/project/issues?id=opentdf_tests&issues=AZy-wx_XJdvnu9AEKzwB&open=AZy-wx_XJdvnu9AEKzwB&pullRequest=380
exit $?
;;
*)
echo "Unknown feature: $2"
exit 2
Expand Down
4 changes: 4 additions & 0 deletions xtest/tdfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
feature_type = Literal[
"assertions",
"assertion_verification",
"attribute_traversal",
"audit_logging",
"autoconfigure",
"better-messages-2024",
Expand Down Expand Up @@ -117,6 +118,9 @@ def __init__(self, **kwargs: dict[str, Any]):
if self.semver >= (0, 11, 0):
self.features.add("obligations")

# Included in platform v0.12.0
if self.semver >= (0, 12, 0):
self.features.add("attribute_traversal")
# In ocrypto < 0.10.0, there was a bug that hardcoded to P256 on uncompressing the EC public key,
# even if the key was actually P384 or P521. This was fixed in ocrypto 0.10.0, so we can only support EC
# wrapping with those curves on platforms v0.13.0 and later.
Expand Down
42 changes: 42 additions & 0 deletions xtest/test_abac.py
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,48 @@ def test_autoconfigure_key_management_two_kas_two_keys(
assert filecmp.cmp(pt_file, rt_file)


def test_encrypt_with_missing_value_uses_definition_key(
attribute_missing_value_key_mapping: tuple[str, str],
encrypt_sdk: tdfs.SDK,
tmp_dir: Path,
pt_file: Path,
kas_url_gamma: str,
in_focus: set[tdfs.SDK],
):
"""Encrypts with a missing value FQN and verifies definition-level key mapping."""
if not in_focus & {encrypt_sdk}:
pytest.skip("Not in focus")
tdfs.skip_if_unsupported(
encrypt_sdk, "key_management", "autoconfigure", "attribute_traversal"
)

missing_value_fqn, key_id = attribute_missing_value_key_mapping

sample_name = f"missing-value-def-{encrypt_sdk}"
if sample_name in cipherTexts:
ct_file = cipherTexts[sample_name]
else:
ct_file = tmp_dir / f"{sample_name}.tdf"
encrypt_sdk.encrypt(
pt_file,
ct_file,
mime_type="text/plain",
container="ztdf",
attr_values=[missing_value_fqn],
)
cipherTexts[sample_name] = ct_file

manifest = tdfs.manifest(ct_file)
policy = manifest.encryptionInformation.policy_object
assert policy.body.dataAttributes is not None
assert missing_value_fqn in [v.attribute for v in policy.body.dataAttributes]

assert len(manifest.encryptionInformation.keyAccess) == 1
kao = manifest.encryptionInformation.keyAccess[0]
assert kao.url == kas_url_gamma
assert kao.kid == key_id


def test_import_legacy_golden_r1_key_and_decrypt_no_split(
legacy_imported_golden_r1_key,
decrypt_sdk: tdfs.SDK,
Expand Down
Loading