Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Suggestion: Include a Reason When Constraints Cannot Be Applied #2630

57 changes: 36 additions & 21 deletions aries_cloudagent/protocols/present_proof/dif/pres_exch_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -1400,12 +1400,15 @@ async def __verify_desc_map_list(
f"{desc_map_item_path} path in descriptor_map not applicable"
)
for match_item in match:
if not await self.apply_constraint_received_cred(
constraint, match_item.value
):
try:
await self.apply_constraint_received_cred(
constraint, match_item.value
)
except BaseError as err:
raise DIFPresExchError(
f"Constraint specified for {desc_map_item_id} does not "
f"apply to the enclosed credential in {desc_map_item_path}"
f" Reason: {err.message}"
)
if (
not len(
Expand Down Expand Up @@ -1437,8 +1440,9 @@ async def restrict_field_paths_one_of_filter(

async def apply_constraint_received_cred(
self, constraint: Constraints, cred_dict: dict
) -> bool:
) -> None:
"""Evaluate constraint from the request against received credential."""
"""If successful, returns None, else raises an error with reason for failure"""
fields = constraint._fields
field_paths = []
credential = self.create_vcrecord(cred_dict)
Expand All @@ -1447,7 +1451,10 @@ async def apply_constraint_received_cred(
if is_limit_disclosure:
field = await self.get_updated_field(field, cred_dict)
if not await self.filter_by_field(field, credential):
return False
raise DIFPresExchError(
"Credential is not applicable for field "
f"{field.id} with paths {field.paths}"
)
field_paths = field_paths + (
await self.restrict_field_paths_one_of_filter(
field_paths=field.paths, cred_dict=cred_dict
Expand Down Expand Up @@ -1483,31 +1490,39 @@ async def apply_constraint_received_cred(

for attrs in cred_dict.keys():
if attrs not in field_paths:
return False
raise DIFPresExchError(
"No field in constraints for "
f"{'at least one of ' if isinstance(attrs, list) else ''}{attrs}"
)
for nested_attr_key in nested_field_paths:
nested_attr_values = nested_field_paths[nested_attr_key]
extracted = self.nested_get(cred_dict, nested_attr_key)
if isinstance(extracted, dict):
if not self.check_attr_in_extracted_dict(
extracted, nested_attr_values
):
return False
elif isinstance(extracted, list):
for extracted_dict in extracted:
if not self.check_attr_in_extracted_dict(
extracted_dict, nested_attr_values
):
return False
return True
try:
if isinstance(extracted, dict):
self.check_attr_in_extracted_dict(extracted, nested_attr_values)
elif isinstance(extracted, list):
for extracted_dict in extracted:
self.check_attr_in_extracted_dict(
extracted_dict, nested_attr_values
)
except BaseError as err:
raise DIFPresExchError(
f"{err.message} under parent path '$.{nested_attr_key}'"
)
return None

def check_attr_in_extracted_dict(
self, extracted_dict: dict, nested_attr_values: dict
) -> bool:
) -> None:
"""Check if keys of extracted_dict exists in nested_attr_values."""
"""If successful, returns None, else raises an error with reason for failure"""
for attrs in extracted_dict.keys():
if attrs not in nested_attr_values:
return False
return True
raise DIFPresExchError(
"No field in constraints for "
f"{'at least one of ' if isinstance(attrs, list) else ''}{attrs}"
)
return None

def get_dict_keys_from_path(self, derived_cred_dict: dict, path: str) -> List:
"""Return additional_attrs to build nested_field_paths."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3294,7 +3294,7 @@ async def test_apply_constraint_received_cred_path_update(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand Down Expand Up @@ -3330,9 +3330,10 @@ async def test_apply_constraint_received_cred_invalid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert not await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)
with pytest.raises(DIFPresExchError):
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

constraint = {
"limit_disclosure": "required",
Expand All @@ -3348,9 +3349,10 @@ async def test_apply_constraint_received_cred_invalid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert not await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)
with pytest.raises(DIFPresExchError):
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

@pytest.mark.asyncio
@pytest.mark.ursa_bbs_signatures
Expand All @@ -3374,7 +3376,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand All @@ -3392,7 +3394,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand Down Expand Up @@ -3420,7 +3422,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand Down Expand Up @@ -3464,7 +3466,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand All @@ -3482,7 +3484,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand All @@ -3500,7 +3502,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand All @@ -3518,7 +3520,7 @@ async def test_apply_constraint_received_cred_valid(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_2
assert await dif_pres_exch_handler.apply_constraint_received_cred(
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

Expand All @@ -3542,9 +3544,10 @@ async def test_apply_constraint_received_cred_no_sel_disc(self, profile):
test_module.jsonld, "expand", mock.MagicMock()
) as mock_jsonld_expand:
mock_jsonld_expand.return_value = EXPANDED_CRED_FHIR_TYPE_1
assert not await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)
with pytest.raises(DIFPresExchError):
await dif_pres_exch_handler.apply_constraint_received_cred(
constraint=constraint, cred_dict=cred_dict
)

@pytest.mark.asyncio
async def test_get_updated_path(self, profile):
Expand Down