Skip to content

Commit b4f2e87

Browse files
feat(cawg_identity): Add new functions for generating a Serialize-able report for entire manifest store (#920)
1 parent 6663116 commit b4f2e87

File tree

4 files changed

+116
-7
lines changed

4 files changed

+116
-7
lines changed

cawg_identity/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(test)'] }
2424
all-features = true
2525
rustdoc-args = ["--cfg", "docsrs"]
2626

27+
[features]
28+
v1_api = ["c2pa/v1_api"]
29+
2730
[dependencies]
2831
async-trait = "0.1.78"
2932
base64 = "0.22.1"

cawg_identity/src/identity_assertion/assertion.rs

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,21 @@
1111
// specific language governing permissions and limitations under
1212
// each license.
1313

14-
use std::fmt::{Debug, Formatter};
14+
use std::{
15+
collections::HashMap,
16+
fmt::{Debug, Formatter},
17+
};
1518

16-
use c2pa::Manifest;
19+
use c2pa::{Manifest, Reader};
1720
use serde::{Deserialize, Serialize};
1821
use serde_bytes::ByteBuf;
1922

2023
use crate::{
2124
identity_assertion::{
22-
report::{IdentityAssertionReport, IdentityAssertionsForManifest, SignerPayloadReport},
25+
report::{
26+
IdentityAssertionReport, IdentityAssertionsForManifest,
27+
IdentityAssertionsForManifestStore, SignerPayloadReport,
28+
},
2329
signer_payload::SignerPayload,
2430
},
2531
internal::debug_byte_slice::DebugByteSlice,
@@ -116,6 +122,15 @@ impl IdentityAssertion {
116122
manifest: &Manifest,
117123
verifier: &SV,
118124
) -> impl Serialize {
125+
Self::summarize_all_impl(manifest, verifier).await
126+
}
127+
128+
pub(crate) async fn summarize_all_impl<SV: SignatureVerifier>(
129+
manifest: &Manifest,
130+
verifier: &SV,
131+
) -> IdentityAssertionsForManifest<
132+
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
133+
> {
119134
// NOTE: We can't write this using .map(...).collect() because there are async
120135
// calls.
121136
let mut reports: Vec<
@@ -142,6 +157,65 @@ impl IdentityAssertion {
142157
}
143158
}
144159

160+
/// Summarize all of the identity assertions found for a [`ManifestStore`].
161+
///
162+
/// [`ManifestStore`]: c2pa::ManifestStore
163+
#[cfg(feature = "v1_api")]
164+
pub async fn summarize_manifest_store<SV: SignatureVerifier>(
165+
store: &c2pa::ManifestStore,
166+
verifier: &SV,
167+
) -> impl Serialize {
168+
// NOTE: We can't write this using .map(...).collect() because there are async
169+
// calls.
170+
let mut reports: HashMap<
171+
String,
172+
IdentityAssertionsForManifest<
173+
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
174+
>,
175+
> = HashMap::new();
176+
177+
for (id, manifest) in store.manifests() {
178+
let report = Self::summarize_all_impl(manifest, verifier).await;
179+
reports.insert(id.clone(), report);
180+
}
181+
182+
IdentityAssertionsForManifestStore::<
183+
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
184+
> {
185+
assertions_for_manifest: reports,
186+
}
187+
}
188+
189+
/// Summarize all of the identity assertions found for a [`Reader`].
190+
pub async fn summarize_from_reader<SV: SignatureVerifier>(
191+
reader: &Reader,
192+
verifier: &SV,
193+
) -> impl Serialize {
194+
// NOTE: We can't write this using .map(...).collect() because there are async
195+
// calls.
196+
let mut reports: HashMap<
197+
String,
198+
IdentityAssertionsForManifest<
199+
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
200+
>,
201+
> = HashMap::new();
202+
203+
for manifest in reader.iter_manifests() {
204+
let report = Self::summarize_all_impl(manifest, verifier).await;
205+
206+
// TO DO: What to do if manifest doesn't have a label?
207+
if let Some(label) = manifest.label() {
208+
reports.insert(label.to_owned(), report);
209+
}
210+
}
211+
212+
IdentityAssertionsForManifestStore::<
213+
<<SV as SignatureVerifier>::Output as ToCredentialSummary>::CredentialSummary,
214+
> {
215+
assertions_for_manifest: reports,
216+
}
217+
}
218+
145219
/// Using the provided [`SignatureVerifier`], check the validity of this
146220
/// identity assertion.
147221
///

cawg_identity/src/identity_assertion/report.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,33 @@
1111
// specific language governing permissions and limitations under
1212
// each license.
1313

14-
use serde::{ser::SerializeSeq, Serialize};
14+
use std::collections::HashMap;
15+
16+
use serde::{
17+
ser::{SerializeMap, SerializeSeq},
18+
Serialize,
19+
};
1520

1621
use crate::identity_assertion::signer_payload::SignerPayload;
1722

23+
#[doc(hidden)]
24+
pub struct IdentityAssertionsForManifestStore<IAR: Serialize> {
25+
pub(crate) assertions_for_manifest: HashMap<String, IdentityAssertionsForManifest<IAR>>,
26+
}
27+
28+
impl<IAR: Serialize> Serialize for IdentityAssertionsForManifestStore<IAR> {
29+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
30+
where
31+
S: serde::Serializer,
32+
{
33+
let mut map = serializer.serialize_map(Some(self.assertions_for_manifest.len()))?;
34+
for (manifest_id, report) in self.assertions_for_manifest.iter() {
35+
map.serialize_entry(manifest_id, report)?;
36+
}
37+
map.end()
38+
}
39+
}
40+
1841
#[doc(hidden)]
1942
pub struct IdentityAssertionsForManifest<IAR: Serialize> {
2043
pub(crate) assertion_reports: Vec<IdentityAssertionReport<IAR>>,

cawg_identity/src/tests/claim_aggregation/interop.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ async fn adobe_connected_identities() {
3333

3434
let mut test_image = Cursor::new(test_image);
3535

36-
let manifest_store = Reader::from_stream(format, &mut test_image).unwrap();
37-
assert_eq!(manifest_store.validation_status(), None);
36+
let reader = Reader::from_stream(format, &mut test_image).unwrap();
37+
assert_eq!(reader.validation_status(), None);
3838

39-
let manifest = manifest_store.active_manifest().unwrap();
39+
let manifest = reader.active_manifest().unwrap();
4040
let mut ia_iter = IdentityAssertion::from_manifest(manifest);
4141

4242
// Should find exactly one identity assertion.
@@ -82,6 +82,15 @@ async fn adobe_connected_identities() {
8282
}
8383
);
8484

85+
// Check the summary report for the entire manifest store.
86+
let ia_summary = IdentityAssertion::summarize_from_reader(&reader, &isv).await;
87+
let ia_json = serde_json::to_string(&ia_summary).unwrap();
88+
89+
assert_eq!(
90+
ia_json,
91+
r#"{"urn:uuid:b55062ef-96b6-4f6e-bb7d-9c415f130471":[{"sig_type":"cawg.identity_claims_aggregation","referenced_assertions":["c2pa.hash.data"],"named_actor":{"@context":["https://www.w3.org/ns/credentials/v2","https://creator-assertions.github.io/tbd/tbd"],"type":["VerifiableCredential","IdentityClaimsAggregationCredential"],"issuer":"did:web:connected-identities.identity-stage.adobe.com","validFrom":"2024-10-03T21:47:02Z","verifiedIdentities":[{"type":"cawg.social_media","username":"Robert Tiles","uri":"https://net.s2stagehance.com/roberttiles","verifiedAt":"2024-09-24T18:15:11Z","provider":{"id":"https://behance.net","name":"behance"}}],"credentialSchema":[{"id":"https://creator-assertions.github.io/schemas/v1/creator-identity-assertion.json","type":"JSONSchema"}]}}]}"#
92+
);
93+
8594
// Check the summary report for this manifest.
8695
let ia_summary = IdentityAssertion::summarize_all(manifest, &isv).await;
8796
let ia_json = serde_json::to_string(&ia_summary).unwrap();

0 commit comments

Comments
 (0)