Skip to content

Commit eb975b1

Browse files
authored
feat(rust/signed-doc): Include admin roles during SignatureKidRule initialisation (#566)
* update SignatureKidRule initialisation, include admin roles * fix
1 parent 4e7bfde commit eb975b1

File tree

7 files changed

+93
-33
lines changed

7 files changed

+93
-33
lines changed

rust/catalyst-signed-doc-spec/src/signers/roles.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,52 @@
44
#[derive(serde::Deserialize)]
55
#[allow(clippy::missing_docs_in_private_items)]
66
pub struct Roles {
7-
pub user: Vec<Role>,
7+
#[serde(default)]
8+
pub user: Vec<UserRole>,
9+
#[serde(default)]
10+
pub admin: Vec<AdminRole>,
811
}
912

1013
/// Role definition
1114
#[derive(serde::Deserialize)]
1215
#[allow(clippy::missing_docs_in_private_items)]
13-
pub enum Role {
16+
pub enum UserRole {
1417
/// Role 0 - A registered User / Voter - Base Role
1518
Registered,
1619
/// Registered for posting proposals
1720
Proposer,
1821
/// Registered as a rep for voting purposes.
1922
Representative,
2023
}
24+
25+
#[derive(serde::Deserialize)]
26+
#[allow(clippy::missing_docs_in_private_items)]
27+
pub enum AdminRole {
28+
/// Root Certificate Authority role.
29+
#[serde(rename = "Root CA")]
30+
RootCA,
31+
/// Brand Certificate Authority role.
32+
#[serde(rename = "Brand CA")]
33+
BrandCA,
34+
/// Campaign Certificate Authority role.
35+
#[serde(rename = "Campaign CA")]
36+
CampaignCA,
37+
/// Category Certificate Authority role.
38+
#[serde(rename = "Category CA")]
39+
CategoryCA,
40+
/// Root Admin role.
41+
#[serde(rename = "Root Admin")]
42+
RootAdmin,
43+
/// Brand Admin role.
44+
#[serde(rename = "Brand Admin")]
45+
BrandAdmin,
46+
/// Campaign Admin role.
47+
#[serde(rename = "Campaign Admin")]
48+
CampaignAdmin,
49+
/// Category Admin role.
50+
#[serde(rename = "Category Admin")]
51+
CategoryAdmin,
52+
/// Moderator role.
53+
#[serde(rename = "Moderator")]
54+
Moderator,
55+
}

rust/signed_doc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ license.workspace = true
1111
workspace = true
1212

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

1717
catalyst-signed-doc-macro = { version = "0.0.1", path = "../catalyst-signed-doc-macro" }

rust/signed_doc/src/providers.rs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ use ed25519_dalek::VerifyingKey;
77

88
use crate::{CatalystSignedDocument, DocumentRef};
99

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

5052
use super::{
51-
CatalystId, CatalystSignedDocument, CatalystSignedDocumentProvider, VerifyingKey,
52-
VerifyingKeyProvider,
53+
CatalystId, CatalystIdProvider, CatalystSignedDocument, CatalystSignedDocumentProvider,
54+
VerifyingKey,
5355
};
5456
use crate::{DocLocator, DocumentRef};
5557

@@ -123,8 +125,8 @@ pub mod tests {
123125
}
124126
}
125127

126-
impl VerifyingKeyProvider for TestCatalystProvider {
127-
async fn try_get_key(
128+
impl CatalystIdProvider for TestCatalystProvider {
129+
async fn try_get_registered_key(
128130
&self,
129131
kid: &CatalystId,
130132
) -> anyhow::Result<Option<VerifyingKey>> {

rust/signed_doc/src/validator/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::{
1717
PROPOSAL_COMMENT_FORM_TEMPLATE, PROPOSAL_FORM_TEMPLATE, PROPOSAL_SUBMISSION_ACTION,
1818
},
1919
metadata::DocType,
20-
providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider},
20+
providers::{CatalystIdProvider, CatalystSignedDocumentProvider},
2121
validator::rules::{CollaboratorsRule, SignatureRule, TemplateRule},
2222
CatalystSignedDocument, ContentEncoding, ContentType,
2323
};
@@ -58,7 +58,7 @@ fn proposal_rule() -> Rules {
5858
collaborators: CollaboratorsRule::NotSpecified,
5959
content: ContentRule::NotNil,
6060
kid: SignatureKidRule {
61-
allowed_roles: vec![RoleId::Proposer],
61+
allowed_roles: [RoleId::Proposer].into_iter().collect(),
6262
},
6363
signature: SignatureRule { mutlisig: false },
6464
original_author: OriginalAuthorRule,
@@ -105,7 +105,7 @@ fn proposal_comment_rule() -> Rules {
105105
collaborators: CollaboratorsRule::NotSpecified,
106106
content: ContentRule::NotNil,
107107
kid: SignatureKidRule {
108-
allowed_roles: vec![RoleId::Role0],
108+
allowed_roles: [RoleId::Role0].into_iter().collect(),
109109
},
110110
signature: SignatureRule { mutlisig: false },
111111
original_author: OriginalAuthorRule,
@@ -158,7 +158,7 @@ fn proposal_submission_action_rule() -> Rules {
158158
collaborators: CollaboratorsRule::NotSpecified,
159159
content: ContentRule::StaticSchema(ContentSchema::Json(proposal_action_json_schema)),
160160
kid: SignatureKidRule {
161-
allowed_roles: vec![RoleId::Proposer],
161+
allowed_roles: [RoleId::Proposer].into_iter().collect(),
162162
},
163163
signature: SignatureRule { mutlisig: false },
164164
original_author: OriginalAuthorRule,
@@ -196,7 +196,7 @@ pub async fn validate<Provider>(
196196
provider: &Provider,
197197
) -> anyhow::Result<bool>
198198
where
199-
Provider: CatalystSignedDocumentProvider + VerifyingKeyProvider,
199+
Provider: CatalystSignedDocumentProvider + CatalystIdProvider,
200200
{
201201
let Ok(doc_type) = doc.doc_type() else {
202202
doc.report().missing_field(

rust/signed_doc/src/validator/rules/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use futures::FutureExt;
55

66
use crate::{
7-
providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider},
7+
providers::{CatalystIdProvider, CatalystSignedDocumentProvider},
88
CatalystSignedDocument,
99
};
1010

@@ -80,7 +80,7 @@ impl Rules {
8080
provider: &Provider,
8181
) -> anyhow::Result<bool>
8282
where
83-
Provider: CatalystSignedDocumentProvider + VerifyingKeyProvider,
83+
Provider: CatalystSignedDocumentProvider + CatalystIdProvider,
8484
{
8585
let rules = [
8686
self.id.check(doc, provider).boxed(),
@@ -139,7 +139,7 @@ impl Rules {
139139
section: SectionRule::NotSpecified,
140140
collaborators: CollaboratorsRule::NotSpecified,
141141
content: ContentRule::new(&doc_spec.payload)?,
142-
kid: SignatureKidRule::new(&doc_spec.signers.roles),
142+
kid: SignatureKidRule::new(&doc_spec.signers.roles)?,
143143
signature: SignatureRule { mutlisig: false },
144144
original_author: OriginalAuthorRule,
145145
};

rust/signed_doc/src/validator/rules/signature.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use anyhow::Context;
44
use catalyst_types::problem_report::ProblemReport;
55

66
use crate::{
7-
providers::{CatalystSignedDocumentProvider, VerifyingKeyProvider},
7+
providers::{CatalystIdProvider, CatalystSignedDocumentProvider},
88
signature::{tbs_data, Signature},
99
CatalystSignedDocument,
1010
};
@@ -28,7 +28,7 @@ impl SignatureRule {
2828
provider: &Provider,
2929
) -> anyhow::Result<bool>
3030
where
31-
Provider: CatalystSignedDocumentProvider + VerifyingKeyProvider,
31+
Provider: CatalystSignedDocumentProvider + CatalystIdProvider,
3232
{
3333
if doc.signatures().is_empty() {
3434
doc.report().other(
@@ -74,11 +74,11 @@ async fn validate_signature<Provider>(
7474
report: &ProblemReport,
7575
) -> anyhow::Result<bool>
7676
where
77-
Provider: VerifyingKeyProvider,
77+
Provider: CatalystIdProvider,
7878
{
7979
let kid = sign.kid();
8080

81-
let Some(pk) = provider.try_get_key(kid).await? else {
81+
let Some(pk) = provider.try_get_registered_key(kid).await? else {
8282
report.other(
8383
&format!("Missing public key for {kid}."),
8484
"During public key extraction",

rust/signed_doc/src/validator/rules/signature_kid.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Catalyst Signed Document COSE signature `kid` (Catalyst Id) role validation
22
3-
use catalyst_signed_doc_spec::signers::roles::{Role, Roles};
3+
use std::collections::HashSet;
4+
5+
use catalyst_signed_doc_spec::signers::roles::{AdminRole, Roles, UserRole};
46
use catalyst_types::catalyst_id::role_index::RoleId;
57

68
use crate::CatalystSignedDocument;
@@ -9,24 +11,43 @@ use crate::CatalystSignedDocument;
911
#[derive(Debug)]
1012
pub(crate) struct SignatureKidRule {
1113
/// expected `RoleId` values for the `kid` field
12-
pub(crate) allowed_roles: Vec<RoleId>,
14+
pub(crate) allowed_roles: HashSet<RoleId>,
1315
}
1416

1517
impl SignatureKidRule {
1618
/// Generating `SignatureKidRule` from specs
17-
pub(crate) fn new(spec: &Roles) -> Self {
18-
let allowed_roles = spec
19+
pub(crate) fn new(spec: &Roles) -> anyhow::Result<Self> {
20+
let allowed_roles: HashSet<_> = spec
1921
.user
2022
.iter()
2123
.map(|v| {
2224
match v {
23-
Role::Registered => RoleId::Role0,
24-
Role::Proposer => RoleId::Proposer,
25-
Role::Representative => RoleId::DelegatedRepresentative,
25+
UserRole::Registered => RoleId::Role0,
26+
UserRole::Proposer => RoleId::Proposer,
27+
UserRole::Representative => RoleId::DelegatedRepresentative,
2628
}
2729
})
30+
.chain(spec.admin.iter().map(|v| {
31+
match v {
32+
AdminRole::RootCA => RoleId::RootCA,
33+
AdminRole::BrandCA => RoleId::BrandCA,
34+
AdminRole::CampaignCA => RoleId::CampaignCA,
35+
AdminRole::CategoryCA => RoleId::CategoryCA,
36+
AdminRole::RootAdmin => RoleId::RootAdmin,
37+
AdminRole::BrandAdmin => RoleId::BrandAdmin,
38+
AdminRole::CampaignAdmin => RoleId::CampaignAdmin,
39+
AdminRole::CategoryAdmin => RoleId::CategoryAdmin,
40+
AdminRole::Moderator => RoleId::Moderator,
41+
}
42+
}))
2843
.collect();
29-
Self { allowed_roles }
44+
45+
anyhow::ensure!(
46+
!allowed_roles.is_empty(),
47+
"A list of allowed roles cannot be empty"
48+
);
49+
50+
Ok(Self { allowed_roles })
3051
}
3152

3253
/// Field validation rule
@@ -73,7 +94,9 @@ mod tests {
7394
#[tokio::test]
7495
async fn signature_kid_rule_test() {
7596
let mut rule = SignatureKidRule {
76-
allowed_roles: vec![RoleId::Role0, RoleId::DelegatedRepresentative],
97+
allowed_roles: [RoleId::Role0, RoleId::DelegatedRepresentative]
98+
.into_iter()
99+
.collect(),
77100
};
78101

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

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

95-
rule.allowed_roles = vec![RoleId::Proposer];
118+
rule.allowed_roles = [RoleId::Proposer].into_iter().collect();
96119
assert!(!rule.check(&doc).await.unwrap());
97120
}
98121
}

0 commit comments

Comments
 (0)