Skip to content

Commit

Permalink
refactor: cleanup credentials serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
lrazovic committed Jan 27, 2025
1 parent fd15985 commit 01918cb
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 43 deletions.
56 changes: 20 additions & 36 deletions polimec-common/common/src/credentials/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,15 @@ use pallet_timestamp::Now;
use parity_scale_codec::{Decode, Encode};
use scale_info::{prelude::string::String, TypeInfo};
use serde::{de::Error, ser::SerializeStruct, Serializer};
use sp_runtime::{traits::BadOrigin, DeserializeOwned, RuntimeDebug};
use sp_runtime::{traits::BadOrigin, DeserializeOwned};

pub use jwt_compact::{
alg::{Ed25519, VerifyingKey},
Claims as StandardClaims, *,
};
use serde::Deserializer;

#[derive(
Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, Deserialize, Serialize, MaxEncodedLen,
)]
#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, Deserialize, Serialize, MaxEncodedLen)]
#[serde(rename_all = "lowercase")]
pub enum InvestorType {
Retail,
Expand All @@ -54,8 +52,8 @@ parameter_types! {
pub const Institutional: InvestorType = InvestorType::Institutional;
}

#[derive(Clone, Encode, Decode, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, Deserialize)]
pub struct SampleClaims<AccountId> {
#[derive(Clone, Encode, Decode, Eq, PartialEq, TypeInfo, Deserialize)]
pub struct PolimecPayload<AccountId> {
#[serde(rename = "sub")]
pub subject: AccountId,
#[serde(rename = "iss")]
Expand All @@ -75,7 +73,7 @@ impl<T> EnsureOriginWithCredentials<T::RuntimeOrigin> for EnsureInvestor<T>
where
T: frame_system::Config + pallet_timestamp::Config,
{
type Claims = SampleClaims<T::AccountId>;
type Claims = PolimecPayload<T::AccountId>;
type Success = (T::AccountId, Did, InvestorType, Cid);

fn try_origin(
Expand All @@ -86,19 +84,14 @@ where
let Some(who) = origin.clone().into_signer() else { return Err(origin) };
let Ok(token) = Self::verify_token(token, verifying_key) else { return Err(origin) };
let claims = token.claims();
// Get the current timestamp from the pallet_timestamp. It is in milliseconds.
// Get current timestamp from pallet_timestamp (milliseconds)
let Ok(now) = Now::<T>::get().try_into() else { return Err(origin) };
let Some(date_time) = claims.expiration else { return Err(origin) };

let timestamp: u64 = date_time.timestamp_millis().try_into().map_err(|_| origin.clone())?;

if claims.custom.subject == who && timestamp >= now {
return Ok((
who,
claims.custom.did.clone(),
claims.custom.investor_type.clone(),
claims.custom.ipfs_cid.clone(),
));
return Ok((who, claims.custom.did.clone(), claims.custom.investor_type, claims.custom.ipfs_cid.clone()));
}

Err(origin)
Expand All @@ -111,7 +104,7 @@ where
OuterOrigin: OriginTrait,
{
type Success;
type Claims: Clone + Encode + Decode + Eq + PartialEq + Ord + PartialOrd + TypeInfo + DeserializeOwned;
type Claims: Clone + Encode + Decode + Eq + PartialEq + TypeInfo + DeserializeOwned;

fn try_origin(
origin: OuterOrigin,
Expand Down Expand Up @@ -142,49 +135,40 @@ where
D: Deserializer<'de>,
{
String::deserialize(deserializer)
.map(|string| string.as_bytes().to_vec())
.and_then(|vec| vec.try_into().map_err(|_| Error::custom("failed to deserialize")))
.map(|string| string.into_bytes())
.and_then(|vec| BoundedVec::try_from(vec).map_err(|_| Error::custom("DID exceeds length limit")))
}

pub fn from_bounded_cid<'de, D>(deserializer: D) -> Result<Cid, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)
.map(|string| string.as_bytes().to_vec())
.and_then(|vec| vec.try_into().map_err(|_| Error::custom("failed to deserialize")))
.map(|string| string.into_bytes())
.and_then(|vec| BoundedVec::try_from(vec).map_err(|_| Error::custom("CID exceeds length limit")))
}

impl<AccountId> Serialize for SampleClaims<AccountId>
// Key corrected serialization implementation
impl<AccountId> Serialize for PolimecPayload<AccountId>
where
AccountId: Serialize, // Ensure AccountId can be serialized
AccountId: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Define how many fields we are serializing.
let mut state = serializer.serialize_struct("SampleClaims", 5)?;
let mut state = serializer.serialize_struct("PolimecPayload", 5)?;

// Serialize each field.
// Fields like `subject`, `issuer`, and `investor_type` can be serialized directly.
state.serialize_field("sub", &self.subject)?;
state.serialize_field("iss", &self.issuer)?;
// For the `ipfs_cid_string` field, you'd use your custom logic to convert it to a string or another format suitable for serialization.
// Assuming `cid` is a `BoundedVec<u8, ConstU32<96>>` and you're encoding it as a UTF-8 string.
let ipfs_cid_bytes: scale_info::prelude::vec::Vec<u8> = self.ipfs_cid.clone().into(); // Convert BoundedVec to Vec<u8>
let ipfs_cid_string = String::from_utf8_lossy(&ipfs_cid_bytes); // Convert Vec<u8> to String
state.serialize_field("aud", &ipfs_cid_string)?;

state.serialize_field("investor_type", &self.investor_type)?;

// For the `did` field, you'd use your custom logic to convert it to a string or another format suitable for serialization.
// Assuming `did` is a `BoundedVec<u8, ConstU32<57>>` and you're encoding it as a UTF-8 string.
let did_bytes: scale_info::prelude::vec::Vec<u8> = self.did.clone().into(); // Convert BoundedVec to Vec<u8>
let did_string = String::from_utf8_lossy(&did_bytes); // Convert Vec<u8> to String
state.serialize_field("did", &did_string)?;

// End the serialization
// Serialize the `ipfs_cid` and `did` fields as strings.
state
.serialize_field("aud", core::str::from_utf8(&self.ipfs_cid).map_err(|e| serde::ser::Error::custom(e))?)?;
state.serialize_field("did", core::str::from_utf8(&self.did).map_err(|e| serde::ser::Error::custom(e))?)?;
state.end()
}
}
14 changes: 7 additions & 7 deletions polimec-common/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use alloc::{vec, vec::Vec};
use frame_support::{sp_runtime::app_crypto::sp_core::bytes::to_hex, traits::ConstU32, BoundedVec, Parameter};
use jwt_compact::{alg::Ed25519, AlgorithmExt, Header};
use parity_scale_codec::alloc::string::ToString;
use polimec_common::credentials::{Did, InvestorType, SampleClaims, UntrustedToken};
use polimec_common::credentials::{Did, InvestorType, PolimecPayload, UntrustedToken};
use xcm::{
opaque::{v4::Xcm, VersionedXcm},
v4::{Assets, Location, SendError, SendResult, SendXcm, XcmHash},
Expand Down Expand Up @@ -83,7 +83,7 @@ mod jwt_utils {
// Handle optional IPFS CID
let ipfs_cid = ipfs_cid.unwrap_or_else(|| BoundedVec::with_bounded_capacity(96));
let custom_claims =
SampleClaims { subject: account_id, investor_type, issuer: "verifier".to_string(), did, ipfs_cid };
PolimecPayload { subject: account_id, investor_type, issuer: "verifier".to_string(), did, ipfs_cid };

let mut claims = Claims::new(custom_claims);
claims.expiration = Some(Utc.with_ymd_and_hms(2030, 1, 1, 0, 0, 0).unwrap());
Expand All @@ -92,7 +92,7 @@ mod jwt_utils {
UntrustedToken::new(&token_string).expect("Failed to parse the JWT")
}

// The `Serialize` trait is needed to serialize the `account_id` into a `SampleClaims` struct.
// The `Serialize` trait is needed to serialize the `account_id` into a `PolimecPayload` struct.
pub fn get_mock_jwt<AccountId: frame_support::Serialize>(
account_id: AccountId,
investor_type: InvestorType,
Expand All @@ -101,7 +101,7 @@ mod jwt_utils {
create_jwt(account_id, investor_type, did, None)
}

// The `Serialize` trait is needed to serialize the `account_id` into a `SampleClaims` struct.
// The `Serialize` trait is needed to serialize the `account_id` into a `PolimecPayload` struct.
pub fn get_mock_jwt_with_cid<AccountId: frame_support::Serialize>(
account_id: AccountId,
investor_type: InvestorType,
Expand Down Expand Up @@ -189,7 +189,7 @@ mod tests {
alg::{Ed25519, VerifyingKey},
AlgorithmExt,
};
use polimec_common::credentials::{InvestorType, SampleClaims};
use polimec_common::credentials::{InvestorType, PolimecPayload};

#[test]
fn test_get_test_jwt() {
Expand All @@ -202,7 +202,7 @@ mod tests {
)
.unwrap();
let token = get_mock_jwt("0x1234", InvestorType::Institutional, generate_did_from_account(40u64));
let res = Ed25519.validator::<SampleClaims<String>>(&verifying_key).validate(&token);
let res = Ed25519.validator::<PolimecPayload<String>>(&verifying_key).validate(&token);
assert!(res.is_ok());
}

Expand All @@ -224,7 +224,7 @@ mod tests {
generate_did_from_account(40u64),
bounded_cid.clone(),
);
let res = Ed25519.validator::<SampleClaims<String>>(&verifying_key).validate(&token);
let res = Ed25519.validator::<PolimecPayload<String>>(&verifying_key).validate(&token);
assert!(res.is_ok());
let validated_token = res.unwrap();
let claims = validated_token.claims();
Expand Down

0 comments on commit 01918cb

Please sign in to comment.