Skip to content
Merged
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
39 changes: 37 additions & 2 deletions rust/catalyst-signed-doc-spec/src/signers/roles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,52 @@
#[derive(serde::Deserialize)]
#[allow(clippy::missing_docs_in_private_items)]
pub struct Roles {
pub user: Vec<Role>,
#[serde(default)]
pub user: Vec<UserRole>,
#[serde(default)]
pub admin: Vec<AdminRole>,
}

/// Role definition
#[derive(serde::Deserialize)]
#[allow(clippy::missing_docs_in_private_items)]
pub enum Role {
pub enum UserRole {
/// Role 0 - A registered User / Voter - Base Role
Registered,
/// Registered for posting proposals
Proposer,
/// Registered as a rep for voting purposes.
Representative,
}

#[derive(serde::Deserialize)]
#[allow(clippy::missing_docs_in_private_items)]
pub enum AdminRole {
/// Root Certificate Authority role.
#[serde(rename = "Root CA")]
RootCA,
/// Brand Certificate Authority role.
#[serde(rename = "Brand CA")]
BrandCA,
/// Campaign Certificate Authority role.
#[serde(rename = "Campaign CA")]
CampaignCA,
/// Category Certificate Authority role.
#[serde(rename = "Category CA")]
CategoryCA,
/// Root Admin role.
#[serde(rename = "Root Admin")]
RootAdmin,
/// Brand Admin role.
#[serde(rename = "Brand Admin")]
BrandAdmin,
/// Campaign Admin role.
#[serde(rename = "Campaign Admin")]
CampaignAdmin,
/// Category Admin role.
#[serde(rename = "Category Admin")]
CategoryAdmin,
/// Moderator role.
#[serde(rename = "Moderator")]
Moderator,
}
2 changes: 1 addition & 1 deletion rust/signed_doc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ license.workspace = true
workspace = true

[dependencies]
catalyst-types = { version = "0.0.7", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.7" }
catalyst-types = { version = "0.0.8", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "catalyst-types/v0.0.8" }
cbork-utils = { version = "0.0.2", git = "https://github.com/input-output-hk/catalyst-libs.git", tag = "cbork-utils-v0.0.2" }

catalyst-signed-doc-macro = { version = "0.0.1", path = "../catalyst-signed-doc-macro" }
Expand Down
18 changes: 10 additions & 8 deletions rust/signed_doc/src/providers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ use ed25519_dalek::VerifyingKey;

use crate::{CatalystSignedDocument, DocumentRef};

/// `VerifyingKey` Provider trait
pub trait VerifyingKeyProvider: Send + Sync {
/// Try to get `VerifyingKey`
fn try_get_key(
/// `CatalystId` Provider trait
pub trait CatalystIdProvider: Send + Sync {
/// Try to get `VerifyingKey` by the provided `CatalystId` and corresponding `RoleId`
/// and `KeyRotation` Return `None` if the provided `CatalystId` with the
/// corresponding `RoleId` and `KeyRotation` has not been registered.
fn try_get_registered_key(
&self,
kid: &CatalystId,
) -> impl Future<Output = anyhow::Result<Option<VerifyingKey>>> + Send;
Expand Down Expand Up @@ -48,8 +50,8 @@ pub mod tests {
use std::{collections::HashMap, time::Duration};

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

Expand Down Expand Up @@ -123,8 +125,8 @@ pub mod tests {
}
}

impl VerifyingKeyProvider for TestCatalystProvider {
async fn try_get_key(
impl CatalystIdProvider for TestCatalystProvider {
async fn try_get_registered_key(
&self,
kid: &CatalystId,
) -> anyhow::Result<Option<VerifyingKey>> {
Expand Down
10 changes: 5 additions & 5 deletions rust/signed_doc/src/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use crate::{
PROPOSAL_COMMENT_FORM_TEMPLATE, PROPOSAL_FORM_TEMPLATE, PROPOSAL_SUBMISSION_ACTION,
},
metadata::DocType,
providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider},
providers::{CatalystIdProvider, CatalystSignedDocumentProvider},
validator::rules::{CollaboratorsRule, SignatureRule, TemplateRule},
CatalystSignedDocument, ContentEncoding, ContentType,
};
Expand Down Expand Up @@ -58,7 +58,7 @@ fn proposal_rule() -> Rules {
collaborators: CollaboratorsRule::NotSpecified,
content: ContentRule::NotNil,
kid: SignatureKidRule {
allowed_roles: vec![RoleId::Proposer],
allowed_roles: [RoleId::Proposer].into_iter().collect(),
},
signature: SignatureRule { mutlisig: false },
original_author: OriginalAuthorRule,
Expand Down Expand Up @@ -105,7 +105,7 @@ fn proposal_comment_rule() -> Rules {
collaborators: CollaboratorsRule::NotSpecified,
content: ContentRule::NotNil,
kid: SignatureKidRule {
allowed_roles: vec![RoleId::Role0],
allowed_roles: [RoleId::Role0].into_iter().collect(),
},
signature: SignatureRule { mutlisig: false },
original_author: OriginalAuthorRule,
Expand Down Expand Up @@ -158,7 +158,7 @@ fn proposal_submission_action_rule() -> Rules {
collaborators: CollaboratorsRule::NotSpecified,
content: ContentRule::StaticSchema(ContentSchema::Json(proposal_action_json_schema)),
kid: SignatureKidRule {
allowed_roles: vec![RoleId::Proposer],
allowed_roles: [RoleId::Proposer].into_iter().collect(),
},
signature: SignatureRule { mutlisig: false },
original_author: OriginalAuthorRule,
Expand Down Expand Up @@ -196,7 +196,7 @@ pub async fn validate<Provider>(
provider: &Provider,
) -> anyhow::Result<bool>
where
Provider: CatalystSignedDocumentProvider + VerifyingKeyProvider,
Provider: CatalystSignedDocumentProvider + CatalystIdProvider,
{
let Ok(doc_type) = doc.doc_type() else {
doc.report().missing_field(
Expand Down
6 changes: 3 additions & 3 deletions rust/signed_doc/src/validator/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use futures::FutureExt;

use crate::{
providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider},
providers::{CatalystIdProvider, CatalystSignedDocumentProvider},
CatalystSignedDocument,
};

Expand Down Expand Up @@ -80,7 +80,7 @@ impl Rules {
provider: &Provider,
) -> anyhow::Result<bool>
where
Provider: CatalystSignedDocumentProvider + VerifyingKeyProvider,
Provider: CatalystSignedDocumentProvider + CatalystIdProvider,
{
let rules = [
self.id.check(doc, provider).boxed(),
Expand Down Expand Up @@ -139,7 +139,7 @@ impl Rules {
section: SectionRule::NotSpecified,
collaborators: CollaboratorsRule::NotSpecified,
content: ContentRule::new(&doc_spec.payload)?,
kid: SignatureKidRule::new(&doc_spec.signers.roles),
kid: SignatureKidRule::new(&doc_spec.signers.roles)?,
signature: SignatureRule { mutlisig: false },
original_author: OriginalAuthorRule,
};
Expand Down
8 changes: 4 additions & 4 deletions rust/signed_doc/src/validator/rules/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use anyhow::Context;
use catalyst_types::problem_report::ProblemReport;

use crate::{
providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider},
providers::{CatalystIdProvider, CatalystSignedDocumentProvider},
signature::{tbs_data, Signature},
CatalystSignedDocument,
};
Expand All @@ -28,7 +28,7 @@ impl SignatureRule {
provider: &Provider,
) -> anyhow::Result<bool>
where
Provider: CatalystSignedDocumentProvider + VerifyingKeyProvider,
Provider: CatalystSignedDocumentProvider + CatalystIdProvider,
{
if doc.signatures().is_empty() {
doc.report().other(
Expand Down Expand Up @@ -74,11 +74,11 @@ async fn validate_signature<Provider>(
report: &ProblemReport,
) -> anyhow::Result<bool>
where
Provider: VerifyingKeyProvider,
Provider: CatalystIdProvider,
{
let kid = sign.kid();

let Some(pk) = provider.try_get_key(kid).await? else {
let Some(pk) = provider.try_get_registered_key(kid).await? else {
report.other(
&format!("Missing public key for {kid}."),
"During public key extraction",
Expand Down
43 changes: 33 additions & 10 deletions rust/signed_doc/src/validator/rules/signature_kid.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Catalyst Signed Document COSE signature `kid` (Catalyst Id) role validation

use catalyst_signed_doc_spec::signers::roles::{Role, Roles};
use std::collections::HashSet;

use catalyst_signed_doc_spec::signers::roles::{AdminRole, Roles, UserRole};
use catalyst_types::catalyst_id::role_index::RoleId;

use crate::CatalystSignedDocument;
Expand All @@ -9,24 +11,43 @@ use crate::CatalystSignedDocument;
#[derive(Debug)]
pub(crate) struct SignatureKidRule {
/// expected `RoleId` values for the `kid` field
pub(crate) allowed_roles: Vec<RoleId>,
pub(crate) allowed_roles: HashSet<RoleId>,
}

impl SignatureKidRule {
/// Generating `SignatureKidRule` from specs
pub(crate) fn new(spec: &Roles) -> Self {
let allowed_roles = spec
pub(crate) fn new(spec: &Roles) -> anyhow::Result<Self> {
let allowed_roles: HashSet<_> = spec
.user
.iter()
.map(|v| {
match v {
Role::Registered => RoleId::Role0,
Role::Proposer => RoleId::Proposer,
Role::Representative => RoleId::DelegatedRepresentative,
UserRole::Registered => RoleId::Role0,
UserRole::Proposer => RoleId::Proposer,
UserRole::Representative => RoleId::DelegatedRepresentative,
}
})
.chain(spec.admin.iter().map(|v| {
match v {
AdminRole::RootCA => RoleId::RootCA,
AdminRole::BrandCA => RoleId::BrandCA,
AdminRole::CampaignCA => RoleId::CampaignCA,
AdminRole::CategoryCA => RoleId::CategoryCA,
AdminRole::RootAdmin => RoleId::RootAdmin,
AdminRole::BrandAdmin => RoleId::BrandAdmin,
AdminRole::CampaignAdmin => RoleId::CampaignAdmin,
AdminRole::CategoryAdmin => RoleId::CategoryAdmin,
AdminRole::Moderator => RoleId::Moderator,
}
}))
.collect();
Self { allowed_roles }

anyhow::ensure!(
!allowed_roles.is_empty(),
"A list of allowed roles cannot be empty"
);

Ok(Self { allowed_roles })
}

/// Field validation rule
Expand Down Expand Up @@ -73,7 +94,9 @@ mod tests {
#[tokio::test]
async fn signature_kid_rule_test() {
let mut rule = SignatureKidRule {
allowed_roles: vec![RoleId::Role0, RoleId::DelegatedRepresentative],
allowed_roles: [RoleId::Role0, RoleId::DelegatedRepresentative]
.into_iter()
.collect(),
};

let sk = ed25519_dalek::SigningKey::generate(&mut rand::rngs::OsRng);
Expand All @@ -92,7 +115,7 @@ mod tests {

assert!(rule.check(&doc).await.unwrap());

rule.allowed_roles = vec![RoleId::Proposer];
rule.allowed_roles = [RoleId::Proposer].into_iter().collect();
assert!(!rule.check(&doc).await.unwrap());
}
}
Loading