c2pa-rs

A fork of https://github.com/contentauth/c2pa-rs/
git clone git://archive.git.mtrnord.blog/mtrnords-photography-manager/c2pa-rs.git
Log | Files | Refs | README

commit d5b2c2045996b3879a3a9c5051683437a42c67ba
parent e0b7fcd4b707e1170e928d0606e344c9f393f6b8
Author: Gavin  Peacock <gpeacock@adobe.com>
Date:   Mon, 25 Mar 2024 10:31:37 -0700

Adds Action changes field as option vec of serde_json value (#431)

* Adds Action changed field as option vec of serde_json value
There are no Rust APIS for this yet.
But it allows the values to pass through via actions.
* Fix some CBOR assertions were reporting as JSON.
* fix v2 Action test
* remove yaml config support for config due to unsupported yaml-rust crate
Diffstat:
Msdk/Cargo.toml | 2+-
Msdk/src/assertions/actions.rs | 54+++++++++++++++++++++++++++++++++++++++++++-----------
Msdk/src/manifest.rs | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msdk/src/settings.rs | 2+-
4 files changed, 133 insertions(+), 15 deletions(-)

diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml @@ -71,7 +71,7 @@ chrono = { version = "0.4.27", default-features = false, features = [ "wasmbind", ] } ciborium = "0.2.0" -config = "0.14.0" +config = {version = "0.14.0", default-features = false, features = ["json", "json5", "toml", "ron", "ini"]} conv = "0.3.3" coset = "0.3.1" extfmt = "0.1.1" diff --git a/sdk/src/assertions/actions.rs b/sdk/src/assertions/actions.rs @@ -104,12 +104,16 @@ pub struct Action { software_agent: Option<SoftwareAgent>, /// A semicolon-delimited list of the parts of the resource that were changed since the previous event history. + #[serde(skip_serializing_if = "Option::is_none")] + changed: Option<String>, + + /// A list of the regions of interest of the resource that were changed. /// /// If not present, presumed to be undefined. /// When tracking changes and the scope of the changed components is unknown, /// it should be assumed that anything might have changed. #[serde(skip_serializing_if = "Option::is_none")] - changed: Option<String>, + changes: Option<Vec<serde_json::Value>>, /// The value of the `xmpMM:InstanceID` property for the modified (output) resource. #[serde(rename = "instanceId", skip_serializing_if = "Option::is_none")] @@ -152,7 +156,7 @@ impl Action { matches!( self.software_agent, Some(SoftwareAgent::ClaimGeneratorInfo(_)) - ) + ) || self.changes.is_some() // only defined for v2 } /// Returns the label for this action. @@ -703,19 +707,19 @@ pub mod tests { fn test_json_v2_round_trip() { let json = serde_json::json!({ "actions": [ - { + { "action": "c2pa.edited", "parameters": { - "description": "gradient", - "name": "any value" + "description": "gradient", + "name": "any value" }, "softwareAgent": "TestApp" - }, - { + }, + { "action": "c2pa.opened", "instanceId": "xmp.iid:7b57930e-2f23-47fc-affe-0400d70b738d", "parameters": { - "description": "import" + "description": "import" }, "digitalSourceType": "http://cv.iptc.org/newscodes/digitalsourcetype/algorithmicMedia", "softwareAgent": { @@ -723,10 +727,32 @@ pub mod tests { "version": "1.0", "something": "else" }, - }, - { + }, + { "action": "com.joesphoto.filter", - } + }, + { + "action": "c2pa.dubbed", + "changes": [ + { + "description": "translated to klingon", + "region": [ + { + "type": "temporal", + "time": {} + }, + { + "type": "identified", + "item": { + "identifier": "https://bioportal.bioontology.org/ontologies/FMA", + "value": "lips" + } + } + ] + } + ] + } + ], "templates": [ { @@ -755,5 +781,11 @@ pub mod tests { result.actions[0].software_agent().unwrap(), &SoftwareAgent::String("TestApp".to_string()) ); + assert_eq!( + result.actions[3].changes.as_deref().unwrap()[0] + .get("description") + .unwrap(), + "translated to klingon" + ); } } diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs @@ -639,7 +639,13 @@ impl Manifest { _ => { // inject assertions for all other assertions match assertion.decode_data() { - AssertionData::Json(_) | AssertionData::Cbor(_) => { + AssertionData::Cbor(_) => { + let value = assertion.as_json_object()?; + let ma = ManifestAssertion::new(base_label, value) + .set_instance(claim_assertion.instance()); + manifest.assertions.push(ma); + } + AssertionData::Json(_) => { let value = assertion.as_json_object()?; let ma = ManifestAssertion::new(base_label, value) .set_instance(claim_assertion.instance()) @@ -1424,6 +1430,68 @@ pub(crate) mod tests { } #[test] + #[cfg(feature = "file_io")] + /// test assertion validation on actions, should generate an error + fn ws_valid_labeled_assertion() { + // copy an image to use as our target for embedding + let ap = fixture_path(TEST_SMALL_JPEG); + let temp_dir = tempdir().expect("temp dir"); + let test_output = temp_dir_path(&temp_dir, "ws_bad_assertion.jpg"); + std::fs::copy(ap, test_output).expect("copy"); + + let mut manifest = test_manifest(); + + manifest + .add_labeled_assertion( + "c2pa.actions", + &serde_json::json!({ + "actions": [ + { + "action": "c2pa.edited", + "parameters": { + "description": "gradient", + "name": "any value" + }, + "softwareAgent": "TestApp" + }, + { + "action": "c2pa.dubbed", + "changes": [ + { + "description": "translated to klingon", + "region": [ + { + "type": "temporal", + "time": {} + }, + { + "type": "identified", + "item": { + "identifier": "https://bioportal.bioontology.org/ontologies/FMA", + "value": "lips" + } + } + ] + } + ] + } + ] + }), + ) + .expect("add_assertion"); + + // convert to store + let store = manifest.to_store().expect("valid action to_store"); + let m2 = Manifest::from_store(&store, &store.provenance_label().unwrap(), None) + .expect("from_store"); + let actions: Actions = m2 + .find_assertion("c2pa.actions.v2") + .expect("find_assertion"); + assert_eq!(actions.actions()[0].action(), "c2pa.edited"); + assert_eq!(actions.actions()[1].action(), "c2pa.dubbed"); + } + + #[test] fn test_verifiable_credential() { let mut manifest = test_manifest(); let vc: serde_json::Value = serde_json::from_str(TEST_VC).unwrap(); @@ -1974,7 +2042,25 @@ pub(crate) mod tests { "identifier": "sample1.svg" }, "something": "else" - } + }, + "changes": [ + { + "region" : [ + { + "type" : "temporal", + "time" : {} + }, + { + "type" : "identified", + "item" : { + "identifier" : "https://bioportal.bioontology.org/ontologies/FMA", + "value" : "lips" + } + } + ], + "description": "lip synced area" + } + ] } ], "templates": [ diff --git a/sdk/src/settings.rs b/sdk/src/settings.rs @@ -222,7 +222,7 @@ impl Settings { "json5" => FileFormat::Json5, "ini" => FileFormat::Ini, "toml" => FileFormat::Toml, - "yaml" => FileFormat::Yaml, + //"yaml" => FileFormat::Yaml, "ron" => FileFormat::Ron, _ => return Err(Error::UnsupportedType), };