Skip to content

fix(rust/signed-doc): Apply new document ref and fix validation rules #368

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: fix/mod-doc-ref
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rust/signed_doc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub use content::Content;
use coset::{CborSerializable, TaggedCborSerializable};
use decode_context::{CompatibilityPolicy, DecodeContext};
pub use metadata::{
ContentEncoding, ContentType, DocLocator, DocType, DocumentRef, Metadata, Section,
ContentEncoding, ContentType, DocLocator, DocType, DocumentRef, DocumentRefs, Metadata, Section,
};
use minicbor::{decode, encode, Decode, Decoder, Encode};
pub use signature::{CatalystId, Signatures};
Expand Down
10 changes: 4 additions & 6 deletions rust/signed_doc/src/metadata/document_refs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@ mod doc_locator;
mod doc_ref;
use std::{fmt::Display, str::FromStr};

use catalyst_types::{
problem_report::ProblemReport,
uuid::{CborContext, UuidV7},
};
use catalyst_types::uuid::{CborContext, UuidV7};
use coset::cbor::Value;
pub use doc_locator::DocLocator;
pub use doc_ref::DocumentRef;
Expand Down Expand Up @@ -267,6 +264,7 @@ impl<'de> Deserialize<'de> for DocumentRefs {
#[cfg(test)]
mod tests {

use catalyst_types::problem_report::ProblemReport;
use minicbor::Encoder;
use serde_json::json;

Expand Down Expand Up @@ -352,11 +350,11 @@ mod tests {
let doc_refs = DocumentRefs(vec![doc_ref.clone(), doc_ref]);
let mut buffer = Vec::new();
let mut encoder = Encoder::new(&mut buffer);
doc_refs.encode(&mut encoder, &mut report).unwrap();
doc_refs.encode(&mut encoder, &mut ()).unwrap();
let mut decoder = Decoder::new(&buffer);
let mut decoded_context = DecodeContext {
compatibility_policy: CompatibilityPolicy::Accept,
report: &mut report.clone(),
report: &mut report,
};
let decoded_doc_refs = DocumentRefs::decode(&mut decoder, &mut decoded_context).unwrap();
assert_eq!(decoded_doc_refs, doc_refs);
Expand Down
77 changes: 28 additions & 49 deletions rust/signed_doc/src/metadata/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,19 @@ pub(crate) mod utils;
use catalyst_types::{problem_report::ProblemReport, uuid::UuidV7};
pub use content_encoding::ContentEncoding;
pub use content_type::ContentType;
use coset::CborSerializable;
pub use doc_type::DocType;
pub use document_refs::{DocLocator, DocumentRef, DocumentRefs};
use minicbor::{Decode, Decoder};
use minicbor::Decoder;
pub use section::Section;
use strum::IntoDiscriminant as _;
use utils::{cose_protected_header_find, decode_document_field_from_protected_header, CborUuidV7};

use crate::{
decode_context::DecodeContext,
metadata::supported_field::{SupportedField, SupportedLabel},
metadata::{
supported_field::{SupportedField, SupportedLabel},
utils::decode_cose_protected_header_value,
},
};

/// `content_encoding` field COSE key value
Expand Down Expand Up @@ -122,29 +124,26 @@ impl Metadata {

/// Return `ref` field.
#[must_use]
pub fn doc_ref(&self) -> Option<DocumentRef> {
pub fn doc_ref(&self) -> Option<&DocumentRefs> {
self.0
.get(&SupportedLabel::Ref)
.and_then(SupportedField::try_as_ref_ref)
.copied()
}

/// Return `template` field.
#[must_use]
pub fn template(&self) -> Option<DocumentRef> {
pub fn template(&self) -> Option<&DocumentRefs> {
self.0
.get(&SupportedLabel::Template)
.and_then(SupportedField::try_as_template_ref)
.copied()
}

/// Return `reply` field.
#[must_use]
pub fn reply(&self) -> Option<DocumentRef> {
pub fn reply(&self) -> Option<&DocumentRefs> {
self.0
.get(&SupportedLabel::Reply)
.and_then(SupportedField::try_as_reply_ref)
.copied()
}

/// Return `section` field.
Expand All @@ -166,11 +165,10 @@ impl Metadata {

/// Return `parameters` field.
#[must_use]
pub fn parameters(&self) -> Option<DocumentRef> {
pub fn parameters(&self) -> Option<&DocumentRefs> {
self.0
.get(&SupportedLabel::Parameters)
.and_then(SupportedField::try_as_parameters_ref)
.copied()
}

/// Build `Metadata` object from the metadata fields, doing all necessary validation.
Expand Down Expand Up @@ -266,20 +264,6 @@ impl Metadata {
}
}

if let Some(value) = cose_protected_header_find(
protected,
|key| matches!(key, coset::Label::Text(label) if label.eq_ignore_ascii_case(TYPE_KEY)),
)
.and_then(|value| {
DocType::decode(
&mut Decoder::new(&value.clone().to_vec().unwrap_or_default()),
context,
)
.ok()
}) {
metadata_fields.push(SupportedField::Type(value));
}

if let Some(value) = decode_document_field_from_protected_header::<CborUuidV7>(
protected,
ID_KEY,
Expand All @@ -302,30 +286,30 @@ impl Metadata {
metadata_fields.push(SupportedField::Ver(value));
}

if let Some(value) = decode_document_field_from_protected_header(
protected,
REF_KEY,
COSE_DECODING_CONTEXT,
context.report,
// DocType and DocRef now using minicbor decoding.
if let Some(value) = decode_cose_protected_header_value::<DecodeContext, DocType>(
protected, context, TYPE_KEY,
) {
metadata_fields.push(SupportedField::Type(value));
};
if let Some(value) = decode_cose_protected_header_value::<DecodeContext, DocumentRefs>(
protected, context, REF_KEY,
) {
metadata_fields.push(SupportedField::Ref(value));
}
if let Some(value) = decode_document_field_from_protected_header(
};
if let Some(value) = decode_cose_protected_header_value::<DecodeContext, DocumentRefs>(
protected,
context,
TEMPLATE_KEY,
COSE_DECODING_CONTEXT,
context.report,
) {
metadata_fields.push(SupportedField::Template(value));
}
if let Some(value) = decode_document_field_from_protected_header(
protected,
REPLY_KEY,
COSE_DECODING_CONTEXT,
context.report,
if let Some(value) = decode_cose_protected_header_value::<DecodeContext, DocumentRefs>(
protected, context, REPLY_KEY,
) {
metadata_fields.push(SupportedField::Reply(value));
}

if let Some(value) = decode_document_field_from_protected_header(
protected,
SECTION_KEY,
Expand All @@ -343,20 +327,15 @@ impl Metadata {
CATEGORY_ID_KEY,
]
.iter()
.filter_map(|field_name| -> Option<DocumentRef> {
decode_document_field_from_protected_header(
protected,
field_name,
COSE_DECODING_CONTEXT,
context.report,
)
.filter_map(|field_name| -> Option<DocumentRefs> {
decode_cose_protected_header_value(protected, context, field_name)
})
.fold((None, false), |(res, _), v| (Some(v), res.is_some()));
if has_multiple_fields {
context.report.duplicate_field(
"brand_id, campaign_id, category_id",
"Only value at the same time is allowed parameters, brand_id, campaign_id, category_id",
"Validation of parameters field aliases"
"Parameters field",
"Only one parameter can be used at a time: either brand_id, campaign_id, category_id",
COSE_DECODING_CONTEXT
);
}
if let Some(value) = parameters {
Expand Down
18 changes: 9 additions & 9 deletions rust/signed_doc/src/metadata/supported_field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::Deserialize;
use strum::{EnumDiscriminants, EnumTryAs, IntoDiscriminant as _};

use crate::{
metadata::custom_transient_decode_error, ContentEncoding, ContentType, DocType, DocumentRef,
metadata::custom_transient_decode_error, ContentEncoding, ContentType, DocType, DocumentRefs,
Section,
};

Expand Down Expand Up @@ -104,21 +104,21 @@ pub enum SupportedField {
/// `id` field.
Id(UuidV7) = 1,
/// `ref` field.
Ref(DocumentRef) = 2,
Ref(DocumentRefs) = 2,
/// `ver` field.
Ver(UuidV7) = 3,
/// `type` field.
Type(DocType) = 4,
/// `reply` field.
Reply(DocumentRef) = 5,
Reply(DocumentRefs) = 5,
/// `collabs` field.
Collabs(Vec<String>) = 7,
/// `section` field.
Section(Section) = 8,
/// `template` field.
Template(DocumentRef) = 9,
Template(DocumentRefs) = 9,
/// `parameters` field.
Parameters(DocumentRef) = 10,
Parameters(DocumentRefs) = 10,
/// `Content-Encoding` field.
ContentEncoding(ContentEncoding) = 11,
}
Expand Down Expand Up @@ -230,17 +230,17 @@ impl minicbor::Decode<'_, crate::decode_context::DecodeContext<'_>> for Supporte
d.decode_with(&mut catalyst_types::uuid::CborContext::Tagged)
.map(Self::Id)
},
SupportedLabel::Ref => todo!(),
SupportedLabel::Ref => d.decode_with(ctx).map(Self::Ref),
SupportedLabel::Ver => {
d.decode_with(&mut catalyst_types::uuid::CborContext::Tagged)
.map(Self::Ver)
},
SupportedLabel::Type => d.decode_with(ctx).map(Self::Type),
SupportedLabel::Reply => todo!(),
SupportedLabel::Reply => d.decode_with(ctx).map(Self::Reply),
SupportedLabel::Collabs => todo!(),
SupportedLabel::Section => todo!(),
SupportedLabel::Template => todo!(),
SupportedLabel::Parameters => todo!(),
SupportedLabel::Template => d.decode_with(ctx).map(Self::Template),
SupportedLabel::Parameters => d.decode_with(ctx).map(Self::Parameters),
SupportedLabel::ContentEncoding => todo!(),
}?;

Expand Down
13 changes: 13 additions & 0 deletions rust/signed_doc/src/metadata/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@ use catalyst_types::{
uuid::{CborContext, UuidV7},
};
use coset::{CborSerializable, Label, ProtectedHeader};
use minicbor::{Decode, Decoder};

/// Decode cose protected header value using minicbor decoder.
pub(crate) fn decode_cose_protected_header_value<C, T>(
protected: &ProtectedHeader, context: &mut C, label: &str,
) -> Option<T>
where T: for<'a> Decode<'a, C> {
cose_protected_header_find(protected, |key| matches!(key, Label::Text(l) if l == label))
.and_then(|value| {
let bytes = value.clone().to_vec().unwrap_or_default();
Decoder::new(&bytes).decode_with(context).ok()
})
}

/// Find a value for a predicate in the protected header.
pub(crate) fn cose_protected_header_find(
Expand Down
34 changes: 22 additions & 12 deletions rust/signed_doc/src/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub trait VerifyingKeyProvider {

/// `CatalystSignedDocument` Provider trait
pub trait CatalystSignedDocumentProvider: Send + Sync {
/// Try to get `CatalystSignedDocument`
/// Try to get `CatalystSignedDocument`from document reference
fn try_get_doc(
&self, doc_ref: &DocumentRef,
) -> impl Future<Output = anyhow::Result<Option<CatalystSignedDocument>>> + Send;
Expand All @@ -38,24 +38,34 @@ pub mod tests {

use std::{collections::HashMap, time::Duration};

use catalyst_types::uuid::Uuid;

use super::{
CatalystId, CatalystSignedDocument, CatalystSignedDocumentProvider, DocumentRef,
VerifyingKey, VerifyingKeyProvider,
CatalystId, CatalystSignedDocument, CatalystSignedDocumentProvider, VerifyingKey,
VerifyingKeyProvider,
};
use crate::{DocLocator, DocumentRef};

/// Simple testing implementation of `CatalystSignedDocumentProvider`
#[derive(Default)]
pub struct TestCatalystSignedDocumentProvider(HashMap<Uuid, CatalystSignedDocument>);
#[derive(Default, Debug)]

pub struct TestCatalystSignedDocumentProvider(HashMap<DocumentRef, CatalystSignedDocument>);

impl TestCatalystSignedDocumentProvider {
/// Inserts document into the `TestCatalystSignedDocumentProvider`
/// Inserts document into the `TestCatalystSignedDocumentProvider` where
/// if document reference is provided use that value.
/// if not use the id and version of the provided doc.
///
/// # Errors
/// - Missing document id
pub fn add_document(&mut self, doc: CatalystSignedDocument) -> anyhow::Result<()> {
self.0.insert(doc.doc_id()?.uuid(), doc);
/// Returns error if document reference is not provided and its fail to create one
/// from the given doc.
pub fn add_document(
&mut self, doc_ref: Option<DocumentRef>, doc: &CatalystSignedDocument,
) -> anyhow::Result<()> {
if let Some(dr) = doc_ref {
self.0.insert(dr, doc.clone());
} else {
let dr = DocumentRef::new(doc.doc_id()?, doc.doc_ver()?, DocLocator::default());
self.0.insert(dr, doc.clone());
}
Ok(())
}
}
Expand All @@ -64,7 +74,7 @@ pub mod tests {
async fn try_get_doc(
&self, doc_ref: &DocumentRef,
) -> anyhow::Result<Option<CatalystSignedDocument>> {
Ok(self.0.get(&doc_ref.id.uuid()).cloned())
Ok(self.0.get(doc_ref).cloned())
}

fn future_threshold(&self) -> Option<std::time::Duration> {
Expand Down
Loading