From 16b2a31a1be96f449742839a865ebcb4241e3593 Mon Sep 17 00:00:00 2001 From: Eric Scouten Date: Wed, 26 Feb 2025 14:17:23 -0800 Subject: [PATCH] feat: Simplify `StatusTracker` interface (#937) Having `StatusTracker` as a non-object-safe trait made it impossible to use in further trait APIs. See early draft of #936 for an example of this problem. This PR changes `StatusTracker` to use an enum to describe the only two implementations of `StatusTracker` that have ever existed. Remove the `take_errors` API in favor of a new `has_any_error` function. --- .../src/x509/x509_signature_verifier.rs | 4 +- .../crypto/src/cose/certificate_profile.rs | 2 +- internal/crypto/src/cose/ocsp.rs | 6 +- internal/crypto/src/cose/sign1.rs | 2 +- internal/crypto/src/cose/verifier.rs | 6 +- internal/crypto/src/ocsp/mod.rs | 6 +- .../src/tests/cose/certificate_profile.rs | 8 +- internal/crypto/src/tests/ocsp.rs | 10 +- internal/status-tracker/src/lib.rs | 4 +- internal/status-tracker/src/log.rs | 8 +- internal/status-tracker/src/status_tracker.rs | 156 ++++++++++ .../src/status_tracker/detailed.rs | 79 ----- .../status-tracker/src/status_tracker/mod.rs | 99 ------- .../src/status_tracker/one_shot.rs | 43 --- internal/status-tracker/src/tests/log.rs | 10 +- .../src/tests/status_tracker.rs | 18 +- sdk/src/assertions/bmff_hash.rs | 2 +- sdk/src/asset_handlers/bmff_io.rs | 8 +- sdk/src/asset_handlers/c2pa_io.rs | 10 +- sdk/src/claim.rs | 16 +- sdk/src/cose_sign.rs | 6 +- sdk/src/cose_validator.rs | 10 +- sdk/src/ingredient.rs | 12 +- sdk/src/manifest.rs | 6 +- sdk/src/manifest_store.rs | 37 +-- sdk/src/manifest_store_report.rs | 18 +- sdk/src/reader.rs | 10 +- sdk/src/store.rs | 271 +++++++++--------- sdk/src/validation_results.rs | 2 +- sdk/src/validation_status.rs | 5 +- 30 files changed, 410 insertions(+), 464 deletions(-) create mode 100644 internal/status-tracker/src/status_tracker.rs delete mode 100644 internal/status-tracker/src/status_tracker/detailed.rs delete mode 100644 internal/status-tracker/src/status_tracker/mod.rs delete mode 100644 internal/status-tracker/src/status_tracker/one_shot.rs diff --git a/cawg_identity/src/x509/x509_signature_verifier.rs b/cawg_identity/src/x509/x509_signature_verifier.rs index d90120cd6..2ce2cf4e1 100644 --- a/cawg_identity/src/x509/x509_signature_verifier.rs +++ b/cawg_identity/src/x509/x509_signature_verifier.rs @@ -16,7 +16,7 @@ use c2pa_crypto::{ cose::{parse_cose_sign1, CertificateInfo, CoseError, Verifier}, raw_signature::RawSignatureValidationError, }; -use c2pa_status_tracker::DetailedStatusTracker; +use c2pa_status_tracker::StatusTracker; use coset::CoseSign1; use serde::Serialize; @@ -58,7 +58,7 @@ impl SignatureVerifier for X509SignatureVerifier { let verifier = Verifier::IgnoreProfileAndTrustPolicy; // TO DO: Figure out how to provide a validation log. - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let cose_sign1 = parse_cose_sign1(signature, &signer_payload_cbor, &mut validation_log)?; diff --git a/internal/crypto/src/cose/certificate_profile.rs b/internal/crypto/src/cose/certificate_profile.rs index a72d9363b..ddc55b786 100644 --- a/internal/crypto/src/cose/certificate_profile.rs +++ b/internal/crypto/src/cose/certificate_profile.rs @@ -34,7 +34,7 @@ use crate::{asn1::rfc3161::TstInfo, cose::CertificateTrustPolicy}; pub fn check_certificate_profile( certificate_der: &[u8], ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, _tst_info_opt: Option<&TstInfo>, ) -> Result<(), CertificateProfileError> { let (_rem, signcert) = X509Certificate::from_der(certificate_der).map_err(|_err| { diff --git a/internal/crypto/src/cose/ocsp.rs b/internal/crypto/src/cose/ocsp.rs index fa43f545c..69c078fdb 100644 --- a/internal/crypto/src/cose/ocsp.rs +++ b/internal/crypto/src/cose/ocsp.rs @@ -33,7 +33,7 @@ pub fn check_ocsp_status( data: &[u8], fetch_policy: OcspFetchPolicy, ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { match get_ocsp_der(sign1) { Some(ocsp_response_der) => { @@ -76,7 +76,7 @@ fn check_stapled_ocsp_response( ocsp_response_der: &[u8], data: &[u8], ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let time_stamp_info = if _sync { validate_cose_tst_info(sign1, data) @@ -112,7 +112,7 @@ fn fetch_and_check_ocsp_response( sign1: &CoseSign1, data: &[u8], ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { #[cfg(target_arch = "wasm32")] { diff --git a/internal/crypto/src/cose/sign1.rs b/internal/crypto/src/cose/sign1.rs index 6eb2c121e..19e3e52e6 100644 --- a/internal/crypto/src/cose/sign1.rs +++ b/internal/crypto/src/cose/sign1.rs @@ -30,7 +30,7 @@ use crate::{ pub fn parse_cose_sign1( cose_bytes: &[u8], data: &[u8], - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let mut sign1 = ::from_tagged_slice(cose_bytes) .map_err(|coset_error| { diff --git a/internal/crypto/src/cose/verifier.rs b/internal/crypto/src/cose/verifier.rs index bebcae953..113d83347 100644 --- a/internal/crypto/src/cose/verifier.rs +++ b/internal/crypto/src/cose/verifier.rs @@ -68,7 +68,7 @@ impl Verifier<'_> { cose_sign1: &[u8], data: &[u8], additional_data: &[u8], - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let mut sign1 = parse_cose_sign1(cose_sign1, data, validation_log)?; @@ -184,7 +184,7 @@ impl Verifier<'_> { &self, sign1: &CoseSign1, tst_info_res: &Result, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<(), CoseError> { let ctp = match self { Self::VerifyTrustPolicy(ctp) => *ctp, @@ -254,7 +254,7 @@ impl Verifier<'_> { &self, sign1: &CoseSign1, tst_info_res: &Result, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<(), CoseError> { // IMPORTANT: This function assumes that verify_profile has already been called. diff --git a/internal/crypto/src/ocsp/mod.rs b/internal/crypto/src/ocsp/mod.rs index d621f6f22..b2fdb19d8 100644 --- a/internal/crypto/src/ocsp/mod.rs +++ b/internal/crypto/src/ocsp/mod.rs @@ -13,7 +13,7 @@ //! Tools for working with OCSP responses. -use c2pa_status_tracker::{log_item, validation_codes, DetailedStatusTracker, StatusTracker}; +use c2pa_status_tracker::{log_item, validation_codes, StatusTracker}; use chrono::{DateTime, NaiveDateTime, Utc}; use rasn_ocsp::{BasicOcspResponse, CertStatus, OcspResponseStatus}; use rasn_pkix::CrlReason; @@ -53,7 +53,7 @@ impl OcspResponse { pub(crate) fn from_der_checked( der: &[u8], signing_time: Option>, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let mut output = OcspResponse { ocsp_der: der.to_vec(), @@ -79,7 +79,7 @@ impl OcspResponse { return Ok(output); }; - let mut internal_validation_log = DetailedStatusTracker::default(); + let mut internal_validation_log = StatusTracker::default(); let response_data = &basic_response.tbs_response_data; // get OCSP cert chain if available diff --git a/internal/crypto/src/tests/cose/certificate_profile.rs b/internal/crypto/src/tests/cose/certificate_profile.rs index 094821955..b5c2cbbad 100644 --- a/internal/crypto/src/tests/cose/certificate_profile.rs +++ b/internal/crypto/src/tests/cose/certificate_profile.rs @@ -11,9 +11,7 @@ // specific language governing permissions and limitations under // each license. -use c2pa_status_tracker::{ - validation_codes::SIGNING_CREDENTIAL_EXPIRED, DetailedStatusTracker, StatusTracker, -}; +use c2pa_status_tracker::{validation_codes::SIGNING_CREDENTIAL_EXPIRED, StatusTracker}; #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; use x509_parser::pem::Pem; @@ -27,7 +25,7 @@ use crate::cose::{check_certificate_profile, CertificateTrustPolicy}; )] fn expired_cert() { let ctp = CertificateTrustPolicy::default(); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let cert_der = x509_der_from_pem(include_bytes!( "../fixtures/cose/rsa-pss256_key-expired.pub" @@ -51,7 +49,7 @@ fn expired_cert() { fn cert_algorithms() { let ctp = CertificateTrustPolicy::default(); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let es256_cert = x509_der_from_pem(include_bytes!("../fixtures/raw_signature/es256.pub")); let es384_cert = x509_der_from_pem(include_bytes!("../fixtures/raw_signature/es384.pub")); diff --git a/internal/crypto/src/tests/ocsp.rs b/internal/crypto/src/tests/ocsp.rs index 7edc6bbf5..808ca3116 100644 --- a/internal/crypto/src/tests/ocsp.rs +++ b/internal/crypto/src/tests/ocsp.rs @@ -11,7 +11,7 @@ // specific language governing permissions and limitations under // each license. -use c2pa_status_tracker::DetailedStatusTracker; +use c2pa_status_tracker::StatusTracker; use chrono::{TimeZone, Utc}; #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::wasm_bindgen_test; @@ -26,7 +26,7 @@ use crate::ocsp::OcspResponse; fn good() { let rsp_data = include_bytes!("fixtures/ocsp/good.data"); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let test_time = Utc.with_ymd_and_hms(2023, 2, 1, 8, 0, 0).unwrap(); @@ -45,15 +45,13 @@ fn good() { fn revoked() { let rsp_data = include_bytes!("fixtures/ocsp/revoked.data"); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let test_time = Utc.with_ymd_and_hms(2024, 2, 1, 8, 0, 0).unwrap(); let ocsp_data = OcspResponse::from_der_checked(rsp_data, Some(test_time), &mut validation_log).unwrap(); - let errors = validation_log.take_errors(); - assert!(ocsp_data.revoked_at.is_some()); - assert!(!errors.is_empty()); + assert!(validation_log.has_any_error()); } diff --git a/internal/status-tracker/src/lib.rs b/internal/status-tracker/src/lib.rs index 4b80ee0ec..b86877d57 100644 --- a/internal/status-tracker/src/lib.rs +++ b/internal/status-tracker/src/lib.rs @@ -23,9 +23,7 @@ mod log; pub use log::{LogItem, LogKind}; mod status_tracker; -pub use status_tracker::{ - detailed::DetailedStatusTracker, one_shot::OneShotStatusTracker, StatusTracker, -}; +pub use status_tracker::{ErrorBehavior, StatusTracker}; pub mod validation_codes; diff --git a/internal/status-tracker/src/log.rs b/internal/status-tracker/src/log.rs index e82dd28fd..1d6d4d897 100644 --- a/internal/status-tracker/src/log.rs +++ b/internal/status-tracker/src/log.rs @@ -177,14 +177,14 @@ impl LogItem { /// Set the log item kind to [`LogKind::Success`] and add it to the /// [`StatusTracker`]. - pub fn success(mut self, tracker: &mut impl StatusTracker) { + pub fn success(mut self, tracker: &mut StatusTracker) { self.kind = LogKind::Success; tracker.add_non_error(self); } /// Set the log item kind to [`LogKind::Informational`] and add it to the /// [`StatusTracker`]. - pub fn informational(mut self, tracker: &mut impl StatusTracker) { + pub fn informational(mut self, tracker: &mut StatusTracker) { self.kind = LogKind::Informational; tracker.add_non_error(self); } @@ -197,7 +197,7 @@ impl LogItem { /// /// If the implementation is configured to aggregate all log messages, this /// function will return `Ok(())`. - pub fn failure(mut self, tracker: &mut impl StatusTracker, err: E) -> Result<(), E> { + pub fn failure(mut self, tracker: &mut StatusTracker, err: E) -> Result<(), E> { self.kind = LogKind::Failure; self.err_val = Some(format!("{err:?}").into()); tracker.add_error(self, err) @@ -208,7 +208,7 @@ impl LogItem { /// /// Does not return a [`Result`] and thus ignores the [`StatusTracker`] /// error-handling configuration. - pub fn failure_no_throw(mut self, tracker: &mut impl StatusTracker, err: E) { + pub fn failure_no_throw(mut self, tracker: &mut StatusTracker, err: E) { self.kind = LogKind::Failure; self.err_val = Some(format!("{err:?}").into()); diff --git a/internal/status-tracker/src/status_tracker.rs b/internal/status-tracker/src/status_tracker.rs new file mode 100644 index 000000000..54b374613 --- /dev/null +++ b/internal/status-tracker/src/status_tracker.rs @@ -0,0 +1,156 @@ +// Copyright 2022 Adobe. All rights reserved. +// This file is licensed to you under the Apache License, +// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) +// or the MIT license (http://opensource.org/licenses/MIT), +// at your option. + +// Unless required by applicable law or agreed to in writing, +// this software is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or +// implied. See the LICENSE-MIT and LICENSE-APACHE files for the +// specific language governing permissions and limitations under +// each license. + +use std::{fmt::Debug, iter::Iterator}; + +use crate::LogItem; + +/// A `StatusTracker` is used in the validation logic of c2pa-rs and +/// related crates to control error-handling behavior and optionally +/// aggregate log messages as they are generated. +#[derive(Debug, Default)] +pub struct StatusTracker { + error_behavior: ErrorBehavior, + logged_items: Vec, + ingredient_uris: Vec, +} + +impl StatusTracker { + /// Returns a [`StatusTracker`] with the specified [`ErrorBehavior`]. + pub fn with_error_behavior(error_behavior: ErrorBehavior) -> Self { + Self { + error_behavior, + logged_items: vec![], + ingredient_uris: vec![], + } + } + + /// Returns the current list of validation log items. + pub fn logged_items(&self) -> &[LogItem] { + &self.logged_items + } + + /// Returns a list of validation log items that can be mutated if needed. + pub fn logged_items_mut(&mut self) -> &mut [LogItem] { + &mut self.logged_items + } + + /// Appends the contents of another [`StatusTracker`] to this list of + /// validation log items. + pub fn append(&mut self, other: &StatusTracker) { + for log_item in other.logged_items() { + self.add_non_error(log_item.clone()); + } + } + + /// Adds a non-error [`LogItem`] to this status tracker. + /// + /// Primarily intended for use by [`LogItem::success()`] + /// or [`LogItem::informational()`]. + pub fn add_non_error(&mut self, mut log_item: LogItem) { + if let Some(ingredient_uri) = self.ingredient_uris.last() { + log_item.ingredient_uri = Some(ingredient_uri.to_string().into()); + } + self.logged_items.push(log_item); + } + + /// Adds an error-case [`LogItem`] to this status tracker. + /// + /// Will return `Err(err)` if configured to stop immediately on errors or + /// `Ok(())` if configured to continue on errors. _(See [`ErrorBehavior`].)_ + /// + /// Primarily intended for use by [`LogItem::failure()`]. + pub fn add_error(&mut self, mut log_item: LogItem, err: E) -> Result<(), E> { + if let Some(ingredient_uri) = self.ingredient_uris.last() { + log_item.ingredient_uri = Some(ingredient_uri.to_string().into()); + } + + self.logged_items.push(log_item); + + match self.error_behavior { + ErrorBehavior::StopOnFirstError => Err(err), + ErrorBehavior::ContinueWhenPossible => Ok(()), + } + } + + /// Returns the [`LogItem`]s that have error conditions (`err_val` is + /// populated). + /// + /// Removes matching items from the list of log items. + pub fn filter_errors(&self) -> impl Iterator { + self.logged_items() + .iter() + .filter(|item| item.err_val.is_some()) + } + + /// Returns `true` if the validation log contains a specific C2PA status + /// code. + pub fn has_status(&self, val: &str) -> bool { + self.logged_items().iter().any(|vi| { + if let Some(vs) = &vi.validation_status { + vs == val + } else { + false + } + }) + } + + /// Returns `true` if the validation log contains a specific error. + pub fn has_error(&self, err: E) -> bool { + let err_type = format!("{:?}", &err); + self.logged_items().iter().any(|vi| { + if let Some(e) = &vi.err_val { + e == &err_type + } else { + false + } + }) + } + + /// Returns `true` if the validation log contains any error. + pub fn has_any_error(&self) -> bool { + self.filter_errors().next().is_some() + } + + /// Keeps track of the current ingredient URI, if any. + /// + /// The current URI may be added to any log items that are created. + pub fn push_ingredient_uri>(&mut self, uri: S) { + self.ingredient_uris.push(uri.into()); + } + + /// Removes the current ingredient URI, if any. + pub fn pop_ingredient_uri(&mut self) -> Option { + self.ingredient_uris.pop() + } +} + +/// `ErrorBehavior` configures the behavior of [`StatusTracker`] when its +/// [`add_error`] function is called. +/// +/// [`add_error`]: StatusTracker::add_error +#[derive(Debug, Eq, PartialEq)] +pub enum ErrorBehavior { + /// If an error is encountered, stop validation immediately. + StopOnFirstError, + + /// If an error is encountered, log it and continue validation as much as + /// possible. + ContinueWhenPossible, +} + +impl Default for ErrorBehavior { + fn default() -> Self { + Self::ContinueWhenPossible + } +} diff --git a/internal/status-tracker/src/status_tracker/detailed.rs b/internal/status-tracker/src/status_tracker/detailed.rs deleted file mode 100644 index 53233ce76..000000000 --- a/internal/status-tracker/src/status_tracker/detailed.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2022 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use crate::{LogItem, StatusTracker}; - -/// A `DetailedStatusTracker` aggregates all log conditions observed during a -/// validation pass. -/// -/// When [`add_error()`] is called, it will not raise an error. -/// -/// [`add_error()`]: Self::add_error() -#[derive(Debug, Default)] -pub struct DetailedStatusTracker { - logged_items: Vec, - ingredient_uris: Vec, -} - -impl DetailedStatusTracker { - /// Return the [`LogItem`]s that have error conditions (`err_val` is - /// populated). - /// - /// Removes matching items from the list of log items. - pub fn take_errors(&mut self) -> Vec { - let mut output: Vec = Vec::new(); - - let mut i = 0; - while i < self.logged_items.len() { - if self.logged_items[i].err_val.is_some() { - output.push(self.logged_items.remove(i)); - } else { - i += 1; - } - } - output - } -} - -impl StatusTracker for DetailedStatusTracker { - fn logged_items(&self) -> &[LogItem] { - &self.logged_items - } - - fn add_non_error(&mut self, mut log_item: LogItem) { - if let Some(ingredient_uri) = self.ingredient_uris.last() { - log_item.ingredient_uri = Some(ingredient_uri.to_string().into()); - } - self.logged_items.push(log_item); - } - - fn add_error(&mut self, mut log_item: LogItem, _err: E) -> Result<(), E> { - if let Some(ingredient_uri) = self.ingredient_uris.last() { - log_item.ingredient_uri = Some(ingredient_uri.to_string().into()); - } - self.logged_items.push(log_item); - Ok(()) - } - - fn push_ingredient_uri>(&mut self, uri: S) { - self.ingredient_uris.push(uri.into()); - } - - fn pop_ingredient_uri(&mut self) -> Option { - self.ingredient_uris.pop() - } - - fn logged_items_mut(&mut self) -> &mut [LogItem] { - &mut self.logged_items - } -} diff --git a/internal/status-tracker/src/status_tracker/mod.rs b/internal/status-tracker/src/status_tracker/mod.rs deleted file mode 100644 index 035121ba8..000000000 --- a/internal/status-tracker/src/status_tracker/mod.rs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2022 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use std::{fmt::Debug, iter::Iterator}; - -use crate::LogItem; - -/// A `StatusTracker` is used in the validation logic of c2pa-rs and -/// related crates to control error-handling behavior and optionally -/// aggregate log messages as they are generated. -pub trait StatusTracker: Debug + Send { - /// Return the current list of validation log items. - fn logged_items(&self) -> &[LogItem]; - - /// Return the mutable list of validation log items. - fn logged_items_mut(&mut self) -> &mut [LogItem]; - - /// Appends the contents of another [`StatusTracker`] to this list of - /// validation log items. - fn append(&mut self, other: &impl StatusTracker) { - for log_item in other.logged_items() { - self.add_non_error(log_item.clone()); - } - } - - /// Add a non-error [`LogItem`] to this status tracker. - /// - /// Primarily intended for use by [`LogItem::success()`] - /// or [`LogItem::informational()`]. - fn add_non_error(&mut self, log_item: LogItem); - - /// Add an error-case [`LogItem`] to this status tracker. - /// - /// Some implementations are configured to stop immediately on errors. If - /// so, this function will return `Err(err)`. - /// - /// If the implementation is configured to aggregate all log - /// messages, this function returns `Ok(())`. - /// - /// Primarily intended for use by [`LogItem::failure()`]. - fn add_error(&mut self, log_item: LogItem, err: E) -> Result<(), E>; - - /// Return the [`LogItem`]s that have error conditions (`err_val` is - /// populated). - /// - /// Removes matching items from the list of log items. - fn filter_errors(&mut self) -> impl Iterator { - self.logged_items() - .iter() - .filter(|item| item.err_val.is_some()) - } - - /// Return `true` if the validation log contains a specific C2PA status - /// code. - fn has_status(&self, val: &str) -> bool { - self.logged_items().iter().any(|vi| { - if let Some(vs) = &vi.validation_status { - vs == val - } else { - false - } - }) - } - - /// Return `true` if the validation log contains a specific error. - fn has_error(&self, err: E) -> bool { - let err_type = format!("{:?}", &err); - self.logged_items().iter().any(|vi| { - if let Some(e) = &vi.err_val { - e == &err_type - } else { - false - } - }) - } - - /// Keeps track of the current ingredient URI, if any. - /// - /// The current URI may be added to any log items that are created. - fn push_ingredient_uri>(&mut self, _uri: S) {} - - /// Removes the current ingredient URI, if any. - fn pop_ingredient_uri(&mut self) -> Option { - None - } -} - -pub(crate) mod detailed; -pub(crate) mod one_shot; diff --git a/internal/status-tracker/src/status_tracker/one_shot.rs b/internal/status-tracker/src/status_tracker/one_shot.rs deleted file mode 100644 index 7e50418e7..000000000 --- a/internal/status-tracker/src/status_tracker/one_shot.rs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2022 Adobe. All rights reserved. -// This file is licensed to you under the Apache License, -// Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0) -// or the MIT license (http://opensource.org/licenses/MIT), -// at your option. - -// Unless required by applicable law or agreed to in writing, -// this software is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR REPRESENTATIONS OF ANY KIND, either express or -// implied. See the LICENSE-MIT and LICENSE-APACHE files for the -// specific language governing permissions and limitations under -// each license. - -use crate::{LogItem, StatusTracker}; - -/// A `OneShotStatusTracker` will trigger an error upon the first call to its -/// [`add_error`] function, which is designed to abort any unnecessary -/// computation if the overall result is unnecessary. -/// -/// [`add_error`]: Self::add_error -#[derive(Debug, Default)] -pub struct OneShotStatusTracker { - logged_items: Vec, -} - -impl StatusTracker for OneShotStatusTracker { - fn logged_items(&self) -> &[LogItem] { - &self.logged_items - } - - fn add_non_error(&mut self, log_item: LogItem) { - self.logged_items.push(log_item); - } - - fn add_error(&mut self, log_item: LogItem, err: E) -> std::result::Result<(), E> { - self.logged_items.push(log_item); - Err(err) - } - - fn logged_items_mut(&mut self) -> &mut [LogItem] { - &mut self.logged_items - } -} diff --git a/internal/status-tracker/src/tests/log.rs b/internal/status-tracker/src/tests/log.rs index c875369d4..e5333d901 100644 --- a/internal/status-tracker/src/tests/log.rs +++ b/internal/status-tracker/src/tests/log.rs @@ -13,7 +13,7 @@ use std::borrow::Cow; -use crate::{log_item, DetailedStatusTracker, LogItem, LogKind, StatusTracker}; +use crate::{log_item, LogItem, LogKind, StatusTracker}; #[test] fn r#macro() { @@ -66,7 +66,7 @@ fn macro_from_string() { #[test] fn success() { - let mut tracker = DetailedStatusTracker::default(); + let mut tracker = StatusTracker::default(); log_item!("test1", "test item 1", "test func").success(&mut tracker); let log_item = tracker.logged_items().first().unwrap(); @@ -91,7 +91,7 @@ fn success() { #[test] fn informational() { - let mut tracker = DetailedStatusTracker::default(); + let mut tracker = StatusTracker::default(); log_item!("test1", "test item 1", "test func").informational(&mut tracker); let log_item = tracker.logged_items().first().unwrap(); @@ -116,7 +116,7 @@ fn informational() { #[test] fn failure() { - let mut tracker = DetailedStatusTracker::default(); + let mut tracker = StatusTracker::default(); log_item!("test1", "test item 1", "test func") .failure(&mut tracker, "sample error message") .unwrap(); @@ -143,7 +143,7 @@ fn failure() { #[test] fn failure_no_throw() { - let mut tracker = DetailedStatusTracker::default(); + let mut tracker = StatusTracker::default(); log_item!("test1", "test item 1", "test func") .failure_no_throw(&mut tracker, "sample error message"); diff --git a/internal/status-tracker/src/tests/status_tracker.rs b/internal/status-tracker/src/tests/status_tracker.rs index 3ef052b67..03446e442 100644 --- a/internal/status-tracker/src/tests/status_tracker.rs +++ b/internal/status-tracker/src/tests/status_tracker.rs @@ -15,11 +15,11 @@ use std::fmt::{self, Display, Formatter}; mod detailed { use super::SampleError; - use crate::{log_item, DetailedStatusTracker, StatusTracker}; + use crate::{log_item, StatusTracker}; #[test] fn aggregates_errors() { - let mut tracker = DetailedStatusTracker::default(); + let mut tracker = StatusTracker::default(); // Add an item without an error. log_item!("test1", "test item 1", "test func").success(&mut tracker); @@ -41,15 +41,15 @@ mod detailed { assert!(!tracker.has_error("Something Else")); // Verify that one item with error was found. - let errors = tracker.take_errors(); + let errors: Vec<&crate::LogItem> = tracker.filter_errors().collect(); assert_eq!(errors.len(), 1); - assert_eq!(tracker.logged_items().len(), 1); + assert_eq!(tracker.logged_items().len(), 2); } #[test] fn append() { - let mut tracker1 = DetailedStatusTracker::default(); - let mut tracker2 = DetailedStatusTracker::default(); + let mut tracker1 = StatusTracker::default(); + let mut tracker2 = StatusTracker::default(); log_item!("test1", "test item 1", "test func").success(&mut tracker1); @@ -68,13 +68,11 @@ mod detailed { } mod one_shot { - use crate::{ - log_item, tests::status_tracker::SampleError, OneShotStatusTracker, StatusTracker, - }; + use crate::{log_item, tests::status_tracker::SampleError, ErrorBehavior, StatusTracker}; #[test] fn stops_on_first_error() { - let mut tracker = OneShotStatusTracker::default(); + let mut tracker = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); // Add an item without error. log_item!("test1", "test item 1", "test func").success(&mut tracker); diff --git a/sdk/src/assertions/bmff_hash.rs b/sdk/src/assertions/bmff_hash.rs index aa70cf6d3..5198e0e45 100644 --- a/sdk/src/assertions/bmff_hash.rs +++ b/sdk/src/assertions/bmff_hash.rs @@ -1278,7 +1278,7 @@ pub mod tests { let mut segment_stream11 = std::fs::File::open(segment_stream_path11).unwrap(); - let mut log = DetailedStatusTracker::default(); + let mut log = (); let bmff_io = BmffIO::new("mp4"); let bmff_handler = bmff_io.get_reader(); diff --git a/sdk/src/asset_handlers/bmff_io.rs b/sdk/src/asset_handlers/bmff_io.rs index bc57316fb..b7f6bf04f 100644 --- a/sdk/src/asset_handlers/bmff_io.rs +++ b/sdk/src/asset_handlers/bmff_io.rs @@ -1847,20 +1847,20 @@ pub mod tests { io_utils::tempdirectory, test::{fixture_path, temp_dir_path}, }; + #[cfg(all(feature = "v1_api", feature = "file_io"))] #[test] fn test_read_mp4() { - use c2pa_status_tracker::DetailedStatusTracker; + use c2pa_status_tracker::StatusTracker; use crate::store::Store; let ap = fixture_path("video1.mp4"); - let mut log = DetailedStatusTracker::default(); + let mut log = StatusTracker::default(); let store = Store::load_from_asset(&ap, true, &mut log); - let errors = log.take_errors(); - assert!(errors.is_empty()); + assert!(!log.has_any_error()); if let Ok(s) = store { print!("Store: \n{s}"); diff --git a/sdk/src/asset_handlers/c2pa_io.rs b/sdk/src/asset_handlers/c2pa_io.rs index 2505e7536..2bab553aa 100644 --- a/sdk/src/asset_handlers/c2pa_io.rs +++ b/sdk/src/asset_handlers/c2pa_io.rs @@ -147,7 +147,7 @@ pub mod tests { #![allow(clippy::unwrap_used)] use c2pa_crypto::raw_signature::SigningAlg; - use c2pa_status_tracker::OneShotStatusTracker; + use c2pa_status_tracker::{ErrorBehavior, StatusTracker}; use super::{AssetIO, C2paIO, CAIReader, CAIWriter}; use crate::{ @@ -172,8 +172,12 @@ pub mod tests { .save_cai_store(&temp_path, &manifest) .expect("save cai store"); - let store = Store::load_from_asset(&temp_path, false, &mut OneShotStatusTracker::default()) - .expect("loading store"); + let store = Store::load_from_asset( + &temp_path, + false, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .expect("loading store"); let signer = test_signer(SigningAlg::Ps256); diff --git a/sdk/src/claim.rs b/sdk/src/claim.rs index 02fc436f5..77f3bc640 100644 --- a/sdk/src/claim.rs +++ b/sdk/src/claim.rs @@ -21,7 +21,7 @@ use c2pa_crypto::{ cose::{parse_cose_sign1, CertificateInfo, CertificateTrustPolicy, OcspFetchPolicy}, ocsp::OcspResponse, }; -use c2pa_status_tracker::{log_item, OneShotStatusTracker, StatusTracker}; +use c2pa_status_tracker::{log_item, ErrorBehavior, StatusTracker}; use chrono::{DateTime, Utc}; use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer}; use serde_json::{json, Map, Value}; @@ -1688,7 +1688,8 @@ impl Claim { pub fn signature_info(&self) -> Option { let sig = self.signature_val(); let data = self.data().ok()?; - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); if _sync { Some(get_signing_info(sig, &data, &mut validation_log)) @@ -1706,7 +1707,7 @@ impl Claim { is_provenance: bool, cert_check: bool, ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { // Parse COSE signed data (signature) and validate it. let sig = claim.signature_val().clone(); @@ -1795,7 +1796,7 @@ impl Claim { is_provenance: bool, cert_check: bool, ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { // Parse COSE signed data (signature) and validate it. let sig = claim.signature_val(); @@ -1887,7 +1888,8 @@ impl Claim { pub fn get_cert_chain(&self) -> Result> { let sig = self.signature_val(); let data = self.data()?; - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let vi = get_signing_info(sig, &data, &mut validation_log); @@ -1899,7 +1901,7 @@ impl Claim { asset_data: &mut ClaimAssetData<'_>, is_provenance: bool, verified: Result, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { const UNNAMED: &str = "unnamed"; let default_str = |s: &String| s.clone(); @@ -2816,7 +2818,7 @@ pub(crate) fn check_ocsp_status( sign1: &coset::CoseSign1, data: &[u8], ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { // Moved here instead of c2pa-crypto because of the dependency on settings. diff --git a/sdk/src/cose_sign.rs b/sdk/src/cose_sign.rs index a39a0ed51..ca800d4f1 100644 --- a/sdk/src/cose_sign.rs +++ b/sdk/src/cose_sign.rs @@ -22,7 +22,7 @@ use c2pa_crypto::{ raw_signature::{AsyncRawSigner, RawSigner, RawSignerError, SigningAlg}, time_stamp::{AsyncTimeStampProvider, TimeStampError, TimeStampProvider}, }; -use c2pa_status_tracker::OneShotStatusTracker; +use c2pa_status_tracker::{ErrorBehavior, StatusTracker}; use crate::{ claim::Claim, cose_validator::verify_cose, settings::get_settings_value, AsyncSigner, Error, @@ -71,7 +71,7 @@ pub fn sign_claim(claim_bytes: &[u8], signer: &dyn Signer, box_size: usize) -> R match signed_bytes { Ok(signed_bytes) => { // Sanity check: Ensure that this signature is valid. - let mut cose_log = OneShotStatusTracker::default(); + let mut cose_log = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let passthrough_cap = CertificateTrustPolicy::default(); match verify_cose( @@ -141,7 +141,7 @@ pub(crate) fn cose_sign( fn signing_cert_valid(signing_cert: &[u8]) -> Result<()> { // make sure signer certs are valid - let mut cose_log = OneShotStatusTracker::default(); + let mut cose_log = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let mut passthrough_cap = CertificateTrustPolicy::default(); // allow user EKUs through this check if configured diff --git a/sdk/src/cose_validator.rs b/sdk/src/cose_validator.rs index d12066601..b7f7b3f31 100644 --- a/sdk/src/cose_validator.rs +++ b/sdk/src/cose_validator.rs @@ -48,7 +48,7 @@ pub(crate) fn verify_cose( additional_data: &[u8], cert_check: bool, ctp: &CertificateTrustPolicy, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let verifier = if cert_check { match get_settings_value::("verify.verify_trust") { @@ -124,7 +124,7 @@ fn extract_serial_from_cert(cert: &X509Certificate) -> BigUint { pub(crate) fn get_signing_info( cose_bytes: &[u8], data: &[u8], - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> CertificateInfo { let mut date = None; let mut issuer_org = None; @@ -181,7 +181,7 @@ pub(crate) fn get_signing_info( #[cfg(test)] pub mod tests { use c2pa_crypto::raw_signature::SigningAlg; - use c2pa_status_tracker::DetailedStatusTracker; + use c2pa_status_tracker::StatusTracker; use ciborium::Value; use coset::Label; use sha2::digest::generic_array::sequence::Shorten; @@ -192,7 +192,7 @@ pub mod tests { #[test] fn test_no_timestamp() { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let mut claim = crate::claim::Claim::new("extern_sign_test", Some("contentauth"), 1); claim.build().unwrap(); @@ -219,7 +219,7 @@ pub mod tests { time_stamp::{TimeStampError, TimeStampProvider}, }; - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let mut claim = crate::claim::Claim::new("ocsp_sign_test", Some("contentauth"), 1); claim.build().unwrap(); diff --git a/sdk/src/ingredient.rs b/sdk/src/ingredient.rs index 6c5cd2027..72fbc3fcd 100644 --- a/sdk/src/ingredient.rs +++ b/sdk/src/ingredient.rs @@ -18,7 +18,7 @@ use std::{borrow::Cow, io::Cursor}; use async_generic::async_generic; use c2pa_crypto::base64; -use c2pa_status_tracker::{log_item, DetailedStatusTracker, StatusTracker}; +use c2pa_status_tracker::{log_item, StatusTracker}; use log::{debug, error}; #[cfg(feature = "json_schema")] use schemars::JsonSchema; @@ -583,7 +583,7 @@ impl Ingredient { &mut self, result: Result, manifest_bytes: Option>, - validation_log: &impl StatusTracker, + validation_log: &StatusTracker, ) -> Result<()> { let active_manifest = self.active_manifest.as_deref().unwrap_or_default(); match result { @@ -746,7 +746,7 @@ impl Ingredient { // optionally generate a hash so we know if the file has changed ingredient.hash = options.hash(path); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); // retrieve the manifest bytes from embedded, sidecar or remote and convert to store if found let (result, manifest_bytes) = match Store::load_jumbf_from_path(path) { @@ -859,7 +859,7 @@ impl Ingredient { // Internal implementation to avoid code bloat. #[async_generic()] fn add_stream_internal(mut self, format: &str, stream: &mut dyn CAIRead) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); // retrieve the manifest bytes from embedded, sidecar or remote and convert to store if found let jumbf_stream = load_jumbf_from_stream(format, stream); @@ -939,7 +939,7 @@ impl Ingredient { let mut ingredient = Self::from_stream_info(stream, format, "untitled"); stream.rewind()?; - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); // retrieve the manifest bytes from embedded, sidecar or remote and convert to store if found let (result, manifest_bytes) = match Store::load_jumbf_from_stream(format, stream) { @@ -1383,7 +1383,7 @@ impl Ingredient { ) -> Result { let mut ingredient = Self::from_stream_info(stream, format, "untitled"); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let manifest_bytes: Vec = manifest_bytes.into(); // generate a store from the buffer and then validate from the asset path diff --git a/sdk/src/manifest.rs b/sdk/src/manifest.rs index b72cf6460..e757c7fba 100644 --- a/sdk/src/manifest.rs +++ b/sdk/src/manifest.rs @@ -1551,7 +1551,7 @@ pub(crate) mod tests { use c2pa_crypto::raw_signature::SigningAlg; #[cfg(feature = "file_io")] - use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; + use c2pa_status_tracker::StatusTracker; #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; @@ -1825,7 +1825,7 @@ pub(crate) mod tests { let c2pa_data = manifest .embed(&output, &output, signer.as_ref()) .expect("embed"); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store1 = Store::load_from_memory("c2pa", &c2pa_data, true, &mut validation_log) .expect("load from memory"); @@ -1850,7 +1850,7 @@ pub(crate) mod tests { .embed(&output2, &output2, signer.as_ref()) .expect("embed"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let store3 = Store::load_from_asset(&output2, true, &mut report).unwrap(); let claim2 = store3.provenance_claim().unwrap(); diff --git a/sdk/src/manifest_store.rs b/sdk/src/manifest_store.rs index ed6c6108a..a5d93dac1 100644 --- a/sdk/src/manifest_store.rs +++ b/sdk/src/manifest_store.rs @@ -20,7 +20,7 @@ use std::{ use async_generic::async_generic; use c2pa_crypto::base64; -use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; +use c2pa_status_tracker::StatusTracker; #[cfg(feature = "json_schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -148,7 +148,7 @@ impl ManifestStore { /// creates a ManifestStore from a Store with validation #[allow(dead_code)] // async not used without v1 feature #[async_generic] - pub(crate) fn from_store(store: Store, validation_log: &impl StatusTracker) -> ManifestStore { + pub(crate) fn from_store(store: Store, validation_log: &StatusTracker) -> ManifestStore { if _sync { Self::from_store_impl( store, @@ -171,7 +171,7 @@ impl ManifestStore { #[cfg(all(feature = "file_io", feature = "v1_api"))] pub(crate) fn from_store_with_resources( store: Store, - validation_log: &impl StatusTracker, + validation_log: &StatusTracker, resource_path: &Path, ) -> ManifestStore { ManifestStore::from_store_impl(store, validation_log, Some(resource_path)) @@ -180,7 +180,7 @@ impl ManifestStore { // internal implementation of from_store fn from_store_impl( store: Store, - validation_log: &impl StatusTracker, + validation_log: &StatusTracker, #[cfg(feature = "file_io")] resource_path: Option<&Path>, ) -> ManifestStore { let mut validation_results = ValidationResults::from_store(&store, validation_log); @@ -217,7 +217,7 @@ impl ManifestStore { async fn from_store_impl_async( store: Store, - validation_log: &impl StatusTracker, + validation_log: &StatusTracker, #[cfg(feature = "file_io")] resource_path: Option<&Path>, ) -> ManifestStore { let mut validation_results = ValidationResults::from_store(&store, validation_log); @@ -257,11 +257,11 @@ impl ManifestStore { #[deprecated(since = "0.38.0", note = "Please use Reader::from_json() instead")] #[cfg(feature = "v1_api")] pub fn from_manifest(manifest: &Manifest) -> Result { - use c2pa_status_tracker::OneShotStatusTracker; + use c2pa_status_tracker::{ErrorBehavior, StatusTracker}; let store = manifest.to_store()?; Ok(Self::from_store_impl( store, - &OneShotStatusTracker::default(), + &StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), #[cfg(feature = "file_io")] manifest.resources().base_path(), )) @@ -272,7 +272,7 @@ impl ManifestStore { #[cfg(feature = "v1_api")] #[async_generic] pub fn from_bytes(format: &str, image_bytes: &[u8], verify: bool) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let result = if _sync { Store::load_from_memory(format, image_bytes, verify, &mut validation_log) @@ -304,7 +304,7 @@ impl ManifestStore { mut stream: impl Read + Seek + Send, verify: bool, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let manifest_bytes = Store::load_jumbf_from_stream(format, &mut stream)?; let store = Store::from_jumbf(&manifest_bytes, &mut validation_log)?; @@ -344,7 +344,7 @@ impl ManifestStore { #[cfg(feature = "v1_api")] #[deprecated(since = "0.38.0", note = "Please use Reader::from_file() instead")] pub fn from_file>(path: P) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_asset(path.as_ref(), true, &mut validation_log)?; Ok(Self::from_store(store, &validation_log)) @@ -375,7 +375,7 @@ impl ManifestStore { path: P, resource_path: P, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_asset(path.as_ref(), true, &mut validation_log)?; Ok(Self::from_store_with_resources( @@ -400,7 +400,7 @@ impl ManifestStore { fragment_bytes: &[u8], verify: bool, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); match Store::load_fragment_from_memory_async( format, @@ -429,7 +429,7 @@ impl ManifestStore { fragments: &Vec, verify: bool, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let asset_type = crate::jumbf_io::get_supported_file_extension(path.as_ref()) .ok_or(crate::Error::UnsupportedType)?; @@ -480,7 +480,7 @@ impl ManifestStore { format: &str, asset_bytes: &[u8], ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::from_jumbf(manifest_bytes, &mut validation_log)?; Store::verify_store_async( @@ -523,7 +523,7 @@ impl ManifestStore { format: &str, asset_bytes: &[u8], ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::from_jumbf(manifest_bytes, &mut validation_log)?; Store::verify_store( @@ -595,7 +595,7 @@ mod tests { #![allow(clippy::expect_used)] #![allow(clippy::unwrap_used)] - use c2pa_status_tracker::OneShotStatusTracker; + use c2pa_status_tracker::{ErrorBehavior, StatusTracker}; #[cfg(all(target_arch = "wasm32", not(target_os = "wasi")))] use wasm_bindgen_test::*; @@ -613,7 +613,10 @@ mod tests { fn manifest_report() { let store = create_test_store().expect("creating test store"); - let manifest_store = ManifestStore::from_store(store, &OneShotStatusTracker::default()); + let manifest_store = ManifestStore::from_store( + store, + &StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ); assert!(manifest_store.active_manifest.is_some()); assert!(!manifest_store.manifests.is_empty()); let manifest = manifest_store.get_active().unwrap(); diff --git a/sdk/src/manifest_store_report.rs b/sdk/src/manifest_store_report.rs index fff9db23b..59d3809b8 100644 --- a/sdk/src/manifest_store_report.rs +++ b/sdk/src/manifest_store_report.rs @@ -19,7 +19,7 @@ use std::path::Path; use atree::{Arena, Token}; use c2pa_crypto::base64; #[cfg(feature = "v1_api")] -use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; +use c2pa_status_tracker::StatusTracker; use extfmt::Hexlify; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -73,7 +73,7 @@ impl ManifestStoreReport { #[cfg(feature = "file_io")] #[cfg(feature = "v1_api")] pub fn dump_tree>(path: P) -> Result<()> { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = crate::store::Store::load_from_asset(path.as_ref(), true, &mut validation_log)?; let claim = store.provenance_claim().ok_or(crate::Error::ClaimMissing { @@ -113,7 +113,7 @@ impl ManifestStoreReport { #[cfg(feature = "file_io")] #[cfg(feature = "v1_api")] pub fn dump_cert_chain>(path: P) -> Result<()> { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_asset(path.as_ref(), true, &mut validation_log)?; let cert_str = store.get_provenance_cert_chain()?; @@ -130,7 +130,7 @@ impl ManifestStoreReport { #[cfg(feature = "file_io")] #[cfg(feature = "v1_api")] pub fn cert_chain>(path: P) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_asset(path.as_ref(), true, &mut validation_log)?; store.get_provenance_cert_chain() } @@ -138,7 +138,7 @@ impl ManifestStoreReport { /// Returns the certificate used to sign the active manifest. #[cfg(feature = "v1_api")] pub fn cert_chain_from_bytes(format: &str, bytes: &[u8]) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_memory(format, bytes, true, &mut validation_log)?; store.get_provenance_cert_chain() } @@ -147,7 +147,7 @@ impl ManifestStoreReport { #[cfg(feature = "v1_api")] pub(crate) fn from_store_with_log( store: &Store, - validation_log: &impl StatusTracker, + validation_log: &StatusTracker, ) -> Result { let mut report = Self::from_store(store)?; @@ -172,7 +172,7 @@ impl ManifestStoreReport { #[cfg(feature = "v1_api")] /// Creates a ManifestStoreReport from image bytes and a format pub fn from_bytes(format: &str, image_bytes: &[u8]) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_memory(format, image_bytes, true, &mut validation_log)?; Self::from_store_with_log(&store, &validation_log) } @@ -181,7 +181,7 @@ impl ManifestStoreReport { /// Creates a ManifestStoreReport from a file #[cfg(feature = "file_io")] pub fn from_file>(path: P) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let store = Store::load_from_asset(path.as_ref(), true, &mut validation_log)?; Self::from_store_with_log(&store, &validation_log) } @@ -192,7 +192,7 @@ impl ManifestStoreReport { path: P, fragments: &Vec, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let asset_type = crate::jumbf_io::get_supported_file_extension(path.as_ref()) .ok_or(crate::Error::UnsupportedType)?; diff --git a/sdk/src/reader.rs b/sdk/src/reader.rs index f15497950..97ab50df6 100644 --- a/sdk/src/reader.rs +++ b/sdk/src/reader.rs @@ -23,7 +23,7 @@ use std::{ use async_generic::async_generic; use c2pa_crypto::base64; -use c2pa_status_tracker::DetailedStatusTracker; +use c2pa_status_tracker::StatusTracker; #[cfg(feature = "json_schema")] use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -174,7 +174,7 @@ impl Reader { format: &str, mut stream: impl Read + Seek + Send, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); // first we convert the JUMBF into a usable store let store = Store::from_jumbf(c2pa_data, &mut validation_log)?; @@ -210,7 +210,7 @@ impl Reader { mut stream: impl Read + Seek + Send, mut fragment: impl Read + Seek + Send, ) -> Result { - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let manifest_bytes = Store::load_jumbf_from_stream(format, &mut stream)?; let store = Store::from_jumbf(&manifest_bytes, &mut validation_log)?; @@ -238,7 +238,7 @@ impl Reader { ) -> Result { let verify = get_settings_value::("verify.verify_after_reading")?; // defaults to true - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let asset_type = crate::jumbf_io::get_supported_file_extension(path.as_ref()) .ok_or(crate::Error::UnsupportedType)?; @@ -525,7 +525,7 @@ impl Reader { } #[async_generic()] - fn from_store(store: Store, validation_log: &DetailedStatusTracker) -> Self { + fn from_store(store: Store, validation_log: &StatusTracker) -> Self { let mut validation_results = ValidationResults::from_store(&store, validation_log); let active_manifest = store.provenance_label(); diff --git a/sdk/src/store.rs b/sdk/src/store.rs index 38669fa01..999fe63a9 100644 --- a/sdk/src/store.rs +++ b/sdk/src/store.rs @@ -26,9 +26,7 @@ use c2pa_crypto::{ cose::{parse_cose_sign1, CertificateTrustPolicy, TimeStampStorage}, hash::sha256, }; -#[cfg(feature = "v1_api")] -use c2pa_status_tracker::DetailedStatusTracker; -use c2pa_status_tracker::{log_item, OneShotStatusTracker, StatusTracker}; +use c2pa_status_tracker::{log_item, ErrorBehavior, StatusTracker}; use log::error; #[cfg(feature = "v1_api")] @@ -481,7 +479,8 @@ impl Store { let sig = claim.signature_val(); let data = claim.data().ok()?; - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let sign1 = parse_cose_sign1(sig, &data, &mut validation_log).ok()?; if let Ok(info) = check_ocsp_status(&sign1, &data, &self.ctp, &mut validation_log) { @@ -545,7 +544,8 @@ impl Store { get_settings_value::("verify.verify_after_sign") { if verify_after_sign { - let mut cose_log = OneShotStatusTracker::default(); + let mut cose_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let result = if _sync { verify_cose(&sig, &claim_bytes, b"", false, &self.ctp, &mut cose_log) @@ -955,7 +955,7 @@ impl Store { true } - pub fn from_jumbf(buffer: &[u8], validation_log: &mut impl StatusTracker) -> Result { + pub fn from_jumbf(buffer: &[u8], validation_log: &mut StatusTracker) -> Result { if buffer.is_empty() { return Err(Error::JumbfNotFound); } @@ -1293,7 +1293,7 @@ impl Store { store: &Store, claim: &Claim, asset_data: &mut ClaimAssetData<'_>, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { let mut num_parent_ofs = 0; @@ -1408,7 +1408,7 @@ impl Store { store: &Store, claim: &Claim, asset_data: &mut ClaimAssetData<'_>, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { // walk the ingredients for i in claim.ingredient_assertions() { @@ -1492,7 +1492,7 @@ impl Store { pub async fn verify_store_async( store: &Store, asset_data: &mut ClaimAssetData<'_>, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { let claim = match store.provenance_claim() { Some(c) => c, @@ -1522,7 +1522,7 @@ impl Store { pub fn verify_store( store: &Store, asset_data: &mut ClaimAssetData<'_>, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { let claim = match store.provenance_claim() { Some(c) => c, @@ -2202,7 +2202,8 @@ impl Store { None => return Err(Error::UnsupportedType), } - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let jumbf = self.to_jumbf(signer)?; // use temp store so mulitple calls will work (the Store is not finalized this way) @@ -2393,7 +2394,7 @@ impl Store { labels::INGREDIENT, ]; - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let mut store = Store::from_jumbf(manifest_bytes, &mut validation_log)?; // todo: what kinds of validation can we do here since the file is not finailized; @@ -3195,7 +3196,7 @@ impl Store { pub fn verify_from_path( &mut self, asset_path: &'_ Path, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { Store::verify_store(self, &mut ClaimAssetData::Path(asset_path), validation_log) } @@ -3206,7 +3207,7 @@ impl Store { &mut self, buf: &[u8], asset_type: &str, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { Store::verify_store( self, @@ -3221,7 +3222,7 @@ impl Store { &mut self, reader: &mut dyn CAIRead, asset_type: &str, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result<()> { if _sync { Store::verify_store( @@ -3389,7 +3390,7 @@ impl Store { fn load_cai_from_memory( asset_type: &str, data: &[u8], - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let mut input_stream = Cursor::new(data); Store::load_jumbf_from_stream(asset_type, &mut input_stream) @@ -3468,10 +3469,7 @@ impl Store { /// in_path - path to source file /// validation_log - optional vec to contain addition info about the asset #[cfg(all(feature = "v1_api", feature = "file_io"))] - fn load_cai_from_file( - in_path: &Path, - validation_log: &mut impl StatusTracker, - ) -> Result { + fn load_cai_from_file(in_path: &Path, validation_log: &mut StatusTracker) -> Result { match Self::load_jumbf_from_path(in_path) { Ok(manifest_bytes) => { // load and validate with CAI toolkit @@ -3489,7 +3487,7 @@ impl Store { pub fn load_from_asset( asset_path: &Path, verify: bool, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { // load jumbf if available Self::load_cai_from_file(asset_path, validation_log) @@ -3511,7 +3509,7 @@ impl Store { pub fn get_store_from_memory( asset_type: &str, data: &[u8], - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { // load jumbf if available Self::load_cai_from_memory(asset_type, data, validation_log).inspect_err(|e| { @@ -3557,7 +3555,7 @@ impl Store { asset_type: &str, data: &[u8], verify: bool, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { Store::get_store_from_memory(asset_type, data, validation_log).and_then(|store| { // verify the store @@ -3584,7 +3582,7 @@ impl Store { asset_type: &str, data: &[u8], verify: bool, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let store = Store::get_store_from_memory(asset_type, data, validation_log)?; @@ -3614,7 +3612,7 @@ impl Store { init_segment: &mut dyn CAIRead, fragments: &Vec, verify: bool, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let mut init_seg_data = Vec::new(); init_segment.read_to_end(&mut init_seg_data)?; @@ -3646,7 +3644,7 @@ impl Store { format: &str, mut stream: impl Read + Seek + Send, mut fragment: impl Read + Seek + Send, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let manifest_bytes = Store::load_jumbf_from_stream(format, &mut stream)?; let store = Store::from_jumbf(&manifest_bytes, validation_log)?; @@ -3675,7 +3673,7 @@ impl Store { init_segment: &[u8], fragment: &[u8], verify: bool, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { Store::get_store_from_memory(asset_type, init_segment, validation_log).and_then(|store| { // verify the store @@ -3711,7 +3709,7 @@ impl Store { init_segment: &[u8], fragment: &[u8], verify: bool, - validation_log: &mut impl StatusTracker, + validation_log: &mut StatusTracker, ) -> Result { let store = Store::get_store_from_memory(asset_type, init_segment, validation_log)?; @@ -3746,7 +3744,7 @@ impl Store { data: &[u8], redactions: Option>, ) -> Result { - let mut report = OneShotStatusTracker::default(); + let mut report = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let store = Store::from_jumbf(data, &mut report)?; // make sure the claims stores are compatible @@ -3848,7 +3846,7 @@ pub mod tests { use std::{fs, io::Write}; use c2pa_crypto::raw_signature::SigningAlg; - use c2pa_status_tracker::{DetailedStatusTracker, StatusTracker}; + use c2pa_status_tracker::{LogItem, StatusTracker}; use memchr::memmem; use serde::Serialize; use sha2::{Digest, Sha256}; @@ -3947,8 +3945,12 @@ pub mod tests { println!("Provenance: {}\n", store.provenance_path().unwrap()); // read from new file - let new_store = - Store::load_from_asset(&op, true, &mut OneShotStatusTracker::default()).unwrap(); + let new_store = Store::load_from_asset( + &op, + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .unwrap(); // can we get by the ingredient data back let _some_binary_data: Vec = vec![ @@ -3994,8 +3996,12 @@ pub mod tests { assert_eq!(splice_point, restore_point); - Store::load_from_asset(&op, true, &mut OneShotStatusTracker::default()) - .expect("Should still verify"); + Store::load_from_asset( + &op, + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .expect("Should still verify"); // test patching jumbf - error should be detected @@ -4005,8 +4011,12 @@ pub mod tests { assert_eq!(splice_point, restore_point); - Store::load_from_asset(&op, true, &mut OneShotStatusTracker::default()) - .expect_err("Should not verify"); + Store::load_from_asset( + &op, + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .expect_err("Should not verify"); } #[test] @@ -4065,13 +4075,12 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); // dump store and compare to original for claim in new_store.claims() { @@ -4157,8 +4166,12 @@ pub mod tests { store.save_to_asset(&ap, signer.as_ref(), &op).unwrap(); // read from new file - let new_store = - Store::load_from_asset(&op, true, &mut OneShotStatusTracker::default()).unwrap(); + let new_store = Store::load_from_asset( + &op, + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .unwrap(); // can we get by the ingredient data back @@ -4351,7 +4364,7 @@ pub mod tests { println!("Provenance: {}\n", store.provenance_path().unwrap()); // make sure we can read from new file - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_asset(&op, false, &mut report).unwrap(); Store::verify_store_async( &new_store, @@ -4361,8 +4374,7 @@ pub mod tests { .await .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[cfg(feature = "v1_api")] @@ -4390,7 +4402,7 @@ pub mod tests { .unwrap(); // make sure we can read from new file - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_asset(&op, false, &mut report).unwrap(); Store::verify_store_async( @@ -4401,8 +4413,7 @@ pub mod tests { .await .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[test] @@ -4441,7 +4452,7 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4537,7 +4548,7 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4610,7 +4621,7 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4684,7 +4695,7 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4758,7 +4769,7 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4832,7 +4843,7 @@ pub mod tests { // write to new file println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4891,7 +4902,7 @@ pub mod tests { store.commit_claim(claim1).unwrap(); store.save_to_asset(&ap, signer.as_ref(), &op).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4935,7 +4946,7 @@ pub mod tests { store.commit_claim(claim1).unwrap(); store.save_to_asset(&ap, signer.as_ref(), &op).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -4979,7 +4990,7 @@ pub mod tests { store.commit_claim(claim1).unwrap(); store.save_to_asset(&ap, signer.as_ref(), &op).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); @@ -5014,13 +5025,18 @@ pub mod tests { #[test] fn test_manifest_bad_sig() { let ap = fixture_path("CE-sig-CA.jpg"); - assert!(Store::load_from_asset(&ap, true, &mut OneShotStatusTracker::default()).is_err()); + assert!(Store::load_from_asset( + &ap, + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError) + ) + .is_err()); } #[test] fn test_unsupported_type_without_external_manifest() { let ap = fixture_path("Purple Square.psd"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let result = Store::load_from_asset(&ap, true, &mut report); assert!(matches!(result, Err(Error::UnsupportedType))); println!("Error report for {}: {:?}", ap.display(), report); @@ -5033,7 +5049,7 @@ pub mod tests { fn test_bad_jumbf() { // test bad jumbf let ap = fixture_path("prerelease.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _r = Store::load_from_asset(&ap, true, &mut report); // error report @@ -5047,7 +5063,7 @@ pub mod tests { fn test_detect_byte_change() { // test bad jumbf let ap = fixture_path("XCA.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); Store::load_from_asset(&ap, true, &mut report).unwrap(); // error report @@ -5061,7 +5077,7 @@ pub mod tests { #[cfg(feature = "file_io")] fn test_file_not_found() { let ap = fixture_path("this_does_not_exist.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _result = Store::load_from_asset(&ap, true, &mut report); println!( @@ -5069,15 +5085,17 @@ pub mod tests { ap.display(), report.logged_items() ); + assert!(!report.logged_items().is_empty()); - let errors = report.take_errors(); + + let errors: Vec<&LogItem> = report.filter_errors().collect(); assert!(errors[0].err_val.as_ref().unwrap().starts_with("IoError")); } #[test] fn test_old_manifest() { let ap = fixture_path("prerelease.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _r = Store::load_from_asset(&ap, true, &mut report); println!( @@ -5085,8 +5103,10 @@ pub mod tests { ap.display(), report.logged_items() ); + assert!(!report.logged_items().is_empty()); - let errors = report.take_errors(); + + let errors: Vec<&LogItem> = report.filter_errors().collect(); assert!(errors[0] .err_val .as_ref() @@ -5115,9 +5135,12 @@ pub mod tests { .unwrap(); // read back in - let restored_store = - Store::load_from_asset(op.as_path(), true, &mut OneShotStatusTracker::default()) - .unwrap(); + let restored_store = Store::load_from_asset( + op.as_path(), + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .unwrap(); let pc = restored_store.provenance_claim().unwrap(); @@ -5153,9 +5176,12 @@ pub mod tests { .unwrap(); // read back in - let restored_store = - Store::load_from_asset(op.as_path(), true, &mut OneShotStatusTracker::default()) - .unwrap(); + let restored_store = Store::load_from_asset( + op.as_path(), + true, + &mut StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError), + ) + .unwrap(); let pc = restored_store.provenance_claim().unwrap(); @@ -5177,11 +5203,11 @@ pub mod tests { fixture_name: &str, search_bytes: &[u8], replace_bytes: &[u8], - ) -> impl StatusTracker { + ) -> StatusTracker { let temp_dir = tempdirectory().expect("temp dir"); let path = temp_fixture_path(&temp_dir, fixture_name); patch_file(&path, search_bytes, replace_bytes).expect("patch_file"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _r = Store::load_from_asset(&path, true, &mut report); // errs are in report println!("report: {report:?}"); report @@ -5207,7 +5233,7 @@ pub mod tests { .save_to_asset(ap.as_path(), signer.as_ref(), op.as_path()) .unwrap(); - let mut report = OneShotStatusTracker::default(); + let mut report = StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); // read back in let mut restored_store = Store::load_from_asset(op.as_path(), true, &mut report).unwrap(); @@ -5275,7 +5301,7 @@ pub mod tests { #[test] fn test_assertion_hash_mismatch() { // modifies content of an action assertion - causes an assertion hashuri mismatch - let mut report = patch_and_report("CA.jpg", b"brightnesscontrast", b"brightnesscontraxx"); + let report = patch_and_report("CA.jpg", b"brightnesscontrast", b"brightnesscontraxx"); let first_error = report.filter_errors().next().cloned().unwrap(); assert_eq!( @@ -5292,7 +5318,7 @@ pub mod tests { b"c2pa_manifest\xA3\x63url\x78\x4aself#jumbf=/c2pa/contentauth:urn:uuid:"; const REPLACE_BYTES: &[u8] = b"c2pa_manifest\xA3\x63url\x78\x4aself#jumbf=/c2pa/contentauth:urn:uuix:"; - let mut report = patch_and_report("CIE-sig-CA.jpg", SEARCH_BYTES, REPLACE_BYTES); + let report = patch_and_report("CIE-sig-CA.jpg", SEARCH_BYTES, REPLACE_BYTES); let errors: Vec = report.filter_errors().cloned().collect(); assert_eq!( errors[0].validation_status.as_deref(), @@ -5307,9 +5333,8 @@ pub mod tests { #[test] fn test_display() { let ap = fixture_path("CA.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let store = Store::load_from_asset(&ap, true, &mut report).expect("load_from_asset"); - let _errors = report.take_errors(); println!("store = {store}"); } @@ -5318,7 +5343,7 @@ pub mod tests { fn test_legacy_ingredient_hash() { // test 1.0 ingredient hash let ap = fixture_path("legacy_ingredient_hash.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let store = Store::load_from_asset(&ap, true, &mut report).expect("load_from_asset"); println!("store = {store}"); } @@ -5327,7 +5352,7 @@ pub mod tests { fn test_bmff_legacy() { // test 1.0 bmff hash let ap = fixture_path("legacy.mp4"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let store = Store::load_from_asset(&ap, true, &mut report).expect("load_from_asset"); println!("store = {store}"); } @@ -5341,7 +5366,7 @@ pub mod tests { let init_stream = std::fs::read(init_stream_path).unwrap(); let segment_stream = std::fs::read(segment_stream_path).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let store = Store::load_fragment_from_memory( "mp4", &init_stream, @@ -5373,14 +5398,12 @@ pub mod tests { store.commit_claim(claim1).unwrap(); store.save_to_asset(&ap, signer.as_ref(), &op).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // can we read back in let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); - let errors = report.take_errors(); - - assert!(errors.is_empty()); + assert!(!report.has_any_error()); println!("store = {new_store}"); } @@ -5414,16 +5437,14 @@ pub mod tests { ) .unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let output_data = output_stream.into_inner(); // can we read back in let _new_store = Store::load_from_memory("mp4", &output_data, true, &mut report).unwrap(); - let errors = report.take_errors(); - - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[test] @@ -5432,7 +5453,7 @@ pub mod tests { // test adding to actual image let ap = fixture_path("no_manifest.jpg"); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // can we read back in let _store = Store::load_from_asset(&ap, true, &mut report); @@ -5474,7 +5495,8 @@ pub mod tests { assert_eq!(saved_manifest, loaded_manifest); // test auto loading of sidecar with validation - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); Store::load_from_asset(&op, true, &mut validation_log).unwrap(); } @@ -5529,7 +5551,8 @@ pub mod tests { assert_eq!(ext_ref, url_string); // make sure it validates - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); Store::load_from_asset(&op, true, &mut validation_log).unwrap(); } @@ -5596,7 +5619,8 @@ pub mod tests { assert_eq!(ext_ref, url_string); // make sure it validates - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); Store::load_from_asset(&op, true, &mut validation_log).unwrap(); } @@ -5645,7 +5669,8 @@ pub mod tests { // Load the exported file into a buffer let file_buffer = std::fs::read(&op).unwrap(); - let mut validation_log = OneShotStatusTracker::default(); + let mut validation_log = + StatusTracker::with_error_behavior(ErrorBehavior::StopOnFirstError); let result = Store::load_from_memory("png", &file_buffer, true, &mut validation_log); assert!(result.is_err()); @@ -5688,7 +5713,7 @@ pub mod tests { result = result_stream.into_inner(); // make sure we can read from new file - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_memory("jpeg", &result, false, &mut report).unwrap(); Store::verify_store_async( @@ -5699,8 +5724,7 @@ pub mod tests { .await .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); // std::fs::write("target/test.jpg", result).unwrap(); } @@ -5739,13 +5763,12 @@ pub mod tests { println!("Provenance: {}\n", store.provenance_path().unwrap()); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); // read from new file let new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); // dump store and compare to original for claim in new_store.claims() { @@ -5851,15 +5874,14 @@ pub mod tests { .unwrap(); output_file.write_all(&out_stream.into_inner()).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_asset(&output, false, &mut report).unwrap(); Store::verify_store_async(&new_store, &mut ClaimAssetData::Path(&output), &mut report) .await .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[test] @@ -5936,11 +5958,10 @@ pub mod tests { .unwrap(); output_file.write_all(&out_stream.into_inner()).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _new_store = Store::load_from_asset(&output, true, &mut report).unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[cfg_attr(not(target_arch = "wasm32"), actix::test)] @@ -6002,15 +6023,14 @@ pub mod tests { output_file.seek(SeekFrom::Start(offset as u64)).unwrap(); output_file.write_all(&cm).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_asset(&output, false, &mut report).unwrap(); Store::verify_store_async(&new_store, &mut ClaimAssetData::Path(&output), &mut report) .await .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[test] @@ -6075,11 +6095,10 @@ pub mod tests { output_file.seek(SeekFrom::Start(offset as u64)).unwrap(); output_file.write_all(&cm).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _new_store = Store::load_from_asset(&output, true, &mut report).unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[test] @@ -6143,11 +6162,10 @@ pub mod tests { output_file.seek(SeekFrom::Start(offset as u64)).unwrap(); output_file.write_all(&cm).unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _new_store = Store::load_from_asset(&output, true, &mut report).unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[cfg(feature = "v1_api")] @@ -6264,11 +6282,10 @@ pub mod tests { ) .unwrap(); - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let _new_store = Store::load_from_asset(&op, true, &mut report).unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); } #[test] @@ -6382,7 +6399,7 @@ pub mod tests { result = result_stream.into_inner(); // make sure we can read from new file - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_memory("jpeg", &result, false, &mut report).unwrap(); println!("new_store: {}", new_store); @@ -6394,8 +6411,7 @@ pub mod tests { ) .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); // std::fs::write("target/test.jpg", result).unwrap(); } @@ -6518,7 +6534,7 @@ pub mod tests { result = result_stream.into_inner(); // make sure we can read from new file - let mut report = DetailedStatusTracker::default(); + let mut report = StatusTracker::default(); let new_store = Store::load_from_memory("jpeg", &result, false, &mut report).unwrap(); println!("new_store: {}", new_store); @@ -6531,8 +6547,7 @@ pub mod tests { .await .unwrap(); - let errors = report.take_errors(); - assert!(errors.is_empty()); + assert!(!report.has_any_error()); // std::fs::write("target/test.jpg", result).unwrap(); } @@ -6591,7 +6606,7 @@ pub mod tests { for entry in &fragments { let file_path = new_output_path.join(entry.file_name().unwrap()); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let mut fragment_stream = std::fs::File::open(&file_path).unwrap(); let _manifest = Store::load_fragment_from_stream( @@ -6602,8 +6617,7 @@ pub mod tests { ) .unwrap(); init_stream.seek(std::io::SeekFrom::Start(0)).unwrap(); - let errors = validation_log.take_errors(); - assert!(errors.is_empty()); + assert!(!validation_log.has_any_error()); } // test verifying all at once @@ -6613,7 +6627,7 @@ pub mod tests { } //let mut reader = Cursor::new(init_stream); - let mut validation_log = DetailedStatusTracker::default(); + let mut validation_log = StatusTracker::default(); let _manifest = Store::load_from_file_and_fragments( "mp4", &mut init_stream, @@ -6623,8 +6637,7 @@ pub mod tests { ) .unwrap(); - let errors = validation_log.take_errors(); - assert!(errors.is_empty()); + assert!(!validation_log.has_any_error()); } Err(_) => panic!("test misconfigures"), } diff --git a/sdk/src/validation_results.rs b/sdk/src/validation_results.rs index 8e6203cfa..ae8f1012b 100644 --- a/sdk/src/validation_results.rs +++ b/sdk/src/validation_results.rs @@ -98,7 +98,7 @@ pub struct ValidationResults { } impl ValidationResults { - pub(crate) fn from_store(store: &Store, validation_log: &impl StatusTracker) -> Self { + pub(crate) fn from_store(store: &Store, validation_log: &StatusTracker) -> Self { let mut results = ValidationResults::default(); let mut statuses: Vec = validation_log diff --git a/sdk/src/validation_status.rs b/sdk/src/validation_status.rs index 89a3fdb53..fcaf8846c 100644 --- a/sdk/src/validation_status.rs +++ b/sdk/src/validation_status.rs @@ -220,10 +220,7 @@ impl PartialEq for ValidationStatus { /// item in the tracker which reflect errors in the active manifest or which would not /// be reported as a validation error for any ingredient. #[cfg(feature = "v1_api")] -pub fn status_for_store( - store: &Store, - validation_log: &impl StatusTracker, -) -> Vec { +pub fn status_for_store(store: &Store, validation_log: &StatusTracker) -> Vec { let validation_results = crate::validation_results::ValidationResults::from_store(store, validation_log); validation_results.validation_errors().unwrap_or_default()