2023-03-30 20:27:17 +00:00
|
|
|
use mitra_models::profiles::types::{
|
|
|
|
ExtraField,
|
|
|
|
IdentityProof,
|
|
|
|
IdentityProofType,
|
|
|
|
PaymentLink,
|
|
|
|
PaymentOption,
|
|
|
|
};
|
2023-03-16 17:59:45 +00:00
|
|
|
use mitra_utils::did::Did;
|
|
|
|
|
2022-07-23 20:21:23 +00:00
|
|
|
use crate::activitypub::vocabulary::{
|
|
|
|
IDENTITY_PROOF,
|
|
|
|
LINK,
|
|
|
|
PROPERTY_VALUE,
|
|
|
|
};
|
2022-07-23 22:02:47 +00:00
|
|
|
use crate::errors::ValidationError;
|
2023-03-14 23:07:30 +00:00
|
|
|
use crate::ethereum::identity::verify_eip191_signature;
|
2022-11-09 19:53:17 +00:00
|
|
|
use crate::identity::{
|
|
|
|
claims::create_identity_claim,
|
2023-03-14 23:07:30 +00:00
|
|
|
minisign::{
|
|
|
|
parse_minisign_signature,
|
|
|
|
verify_minisign_signature,
|
|
|
|
},
|
2023-03-14 20:47:27 +00:00
|
|
|
};
|
|
|
|
use crate::json_signatures::proofs::{
|
|
|
|
PROOF_TYPE_ID_EIP191,
|
|
|
|
PROOF_TYPE_ID_MINISIGN,
|
2022-11-09 19:53:17 +00:00
|
|
|
};
|
2022-12-24 17:09:44 +00:00
|
|
|
use crate::web_client::urls::get_subscription_page_url;
|
2023-03-30 20:27:17 +00:00
|
|
|
|
2022-07-23 22:02:47 +00:00
|
|
|
use super::types::ActorAttachment;
|
|
|
|
|
|
|
|
pub fn attach_identity_proof(
|
|
|
|
proof: IdentityProof,
|
|
|
|
) -> ActorAttachment {
|
2023-03-13 19:31:31 +00:00
|
|
|
let proof_type_str = match proof.proof_type {
|
|
|
|
IdentityProofType::LegacyEip191IdentityProof => PROOF_TYPE_ID_EIP191,
|
|
|
|
IdentityProofType::LegacyMinisignIdentityProof => PROOF_TYPE_ID_MINISIGN,
|
|
|
|
};
|
2022-07-23 22:02:47 +00:00
|
|
|
ActorAttachment {
|
|
|
|
object_type: IDENTITY_PROOF.to_string(),
|
|
|
|
name: proof.issuer.to_string(),
|
|
|
|
value: None,
|
2022-07-23 20:21:23 +00:00
|
|
|
href: None,
|
2023-03-13 19:31:31 +00:00
|
|
|
signature_algorithm: Some(proof_type_str.to_string()),
|
2022-07-23 22:02:47 +00:00
|
|
|
signature_value: Some(proof.value),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_identity_proof(
|
|
|
|
actor_id: &str,
|
|
|
|
attachment: &ActorAttachment,
|
|
|
|
) -> Result<IdentityProof, ValidationError> {
|
|
|
|
if attachment.object_type != IDENTITY_PROOF {
|
|
|
|
return Err(ValidationError("invalid attachment type"));
|
|
|
|
};
|
2023-03-13 19:31:31 +00:00
|
|
|
let proof_type_str = attachment.signature_algorithm.as_ref()
|
|
|
|
.ok_or(ValidationError("missing proof type"))?;
|
|
|
|
let proof_type = match proof_type_str.as_str() {
|
|
|
|
PROOF_TYPE_ID_EIP191 => IdentityProofType::LegacyEip191IdentityProof,
|
|
|
|
PROOF_TYPE_ID_MINISIGN => IdentityProofType::LegacyMinisignIdentityProof,
|
|
|
|
_ => return Err(ValidationError("unsupported proof type")),
|
|
|
|
};
|
2022-11-08 21:59:45 +00:00
|
|
|
let did = attachment.name.parse::<Did>()
|
2022-11-22 22:30:47 +00:00
|
|
|
.map_err(|_| ValidationError("invalid DID"))?;
|
2022-11-09 19:53:17 +00:00
|
|
|
let message = create_identity_claim(actor_id, &did)
|
|
|
|
.map_err(|_| ValidationError("invalid claim"))?;
|
2022-07-23 22:02:47 +00:00
|
|
|
let signature = attachment.signature_value.as_ref()
|
|
|
|
.ok_or(ValidationError("missing signature"))?;
|
2022-11-10 18:05:20 +00:00
|
|
|
match did {
|
|
|
|
Did::Key(ref did_key) => {
|
2023-03-13 19:31:31 +00:00
|
|
|
if !matches!(proof_type, IdentityProofType::LegacyMinisignIdentityProof) {
|
2022-11-22 22:30:47 +00:00
|
|
|
return Err(ValidationError("incorrect proof type"));
|
2022-11-10 18:05:20 +00:00
|
|
|
};
|
2023-03-14 23:07:30 +00:00
|
|
|
let signature_bin = parse_minisign_signature(signature)
|
|
|
|
.map_err(|_| ValidationError("invalid signature encoding"))?;
|
|
|
|
verify_minisign_signature(
|
2022-11-10 18:05:20 +00:00
|
|
|
did_key,
|
|
|
|
&message,
|
2023-03-14 23:07:30 +00:00
|
|
|
&signature_bin,
|
2022-11-10 18:05:20 +00:00
|
|
|
).map_err(|_| ValidationError("invalid identity proof"))?;
|
|
|
|
},
|
|
|
|
Did::Pkh(ref did_pkh) => {
|
2023-03-13 19:31:31 +00:00
|
|
|
if !matches!(proof_type, IdentityProofType::LegacyEip191IdentityProof) {
|
2022-11-22 22:30:47 +00:00
|
|
|
return Err(ValidationError("incorrect proof type"));
|
2022-11-10 18:05:20 +00:00
|
|
|
};
|
2023-03-14 23:07:30 +00:00
|
|
|
verify_eip191_signature(
|
2022-11-10 18:05:20 +00:00
|
|
|
did_pkh,
|
|
|
|
&message,
|
|
|
|
signature,
|
|
|
|
).map_err(|_| ValidationError("invalid identity proof"))?;
|
|
|
|
},
|
|
|
|
};
|
2022-07-23 22:02:47 +00:00
|
|
|
let proof = IdentityProof {
|
|
|
|
issuer: did,
|
2022-11-22 22:30:47 +00:00
|
|
|
proof_type: proof_type,
|
2022-07-23 22:02:47 +00:00
|
|
|
value: signature.to_string(),
|
|
|
|
};
|
|
|
|
Ok(proof)
|
|
|
|
}
|
|
|
|
|
2022-07-23 20:21:23 +00:00
|
|
|
pub fn attach_payment_option(
|
|
|
|
instance_url: &str,
|
2023-03-21 13:47:31 +00:00
|
|
|
username: &str,
|
2022-07-23 20:21:23 +00:00
|
|
|
payment_option: PaymentOption,
|
|
|
|
) -> ActorAttachment {
|
2022-08-25 21:51:18 +00:00
|
|
|
let (name, href) = match payment_option {
|
2022-08-29 19:17:16 +00:00
|
|
|
// Local actors can't have payment links
|
2022-08-25 00:09:37 +00:00
|
|
|
PaymentOption::Link(_) => unimplemented!(),
|
2022-08-29 19:17:16 +00:00
|
|
|
PaymentOption::EthereumSubscription(_) => {
|
2022-08-25 00:09:37 +00:00
|
|
|
let name = "EthereumSubscription".to_string();
|
2023-03-21 13:47:31 +00:00
|
|
|
let href = get_subscription_page_url(instance_url, username);
|
2022-08-25 21:51:18 +00:00
|
|
|
(name, href)
|
2022-07-23 20:21:23 +00:00
|
|
|
},
|
2022-08-25 21:51:18 +00:00
|
|
|
PaymentOption::MoneroSubscription(_) => {
|
|
|
|
let name = "MoneroSubscription".to_string();
|
2023-03-21 13:47:31 +00:00
|
|
|
let href = get_subscription_page_url(instance_url, username);
|
2022-08-25 21:51:18 +00:00
|
|
|
(name, href)
|
|
|
|
},
|
|
|
|
};
|
|
|
|
ActorAttachment {
|
|
|
|
object_type: LINK.to_string(),
|
|
|
|
name: name,
|
|
|
|
value: None,
|
|
|
|
href: Some(href),
|
|
|
|
signature_algorithm: None,
|
|
|
|
signature_value: None,
|
2022-07-23 20:21:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_payment_option(
|
|
|
|
attachment: &ActorAttachment,
|
|
|
|
) -> Result<PaymentOption, ValidationError> {
|
|
|
|
if attachment.object_type != LINK {
|
|
|
|
return Err(ValidationError("invalid attachment type"));
|
|
|
|
};
|
2022-08-25 00:09:37 +00:00
|
|
|
let href = attachment.href.as_ref()
|
|
|
|
.ok_or(ValidationError("href attribute is required"))?
|
|
|
|
.to_string();
|
|
|
|
let payment_option = PaymentOption::Link(PaymentLink {
|
|
|
|
name: attachment.name.clone(),
|
|
|
|
href: href,
|
|
|
|
});
|
2022-07-23 20:21:23 +00:00
|
|
|
Ok(payment_option)
|
|
|
|
}
|
|
|
|
|
2022-07-23 22:02:47 +00:00
|
|
|
pub fn attach_extra_field(
|
|
|
|
field: ExtraField,
|
|
|
|
) -> ActorAttachment {
|
|
|
|
ActorAttachment {
|
|
|
|
object_type: PROPERTY_VALUE.to_string(),
|
|
|
|
name: field.name,
|
|
|
|
value: Some(field.value),
|
2022-07-23 20:21:23 +00:00
|
|
|
href: None,
|
2022-07-23 22:02:47 +00:00
|
|
|
signature_algorithm: None,
|
|
|
|
signature_value: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn parse_extra_field(
|
|
|
|
attachment: &ActorAttachment,
|
|
|
|
) -> Result<ExtraField, ValidationError> {
|
|
|
|
if attachment.object_type != PROPERTY_VALUE {
|
|
|
|
return Err(ValidationError("invalid attachment type"));
|
|
|
|
};
|
|
|
|
let property_value = attachment.value.as_ref()
|
|
|
|
.ok_or(ValidationError("missing property value"))?;
|
|
|
|
let field = ExtraField {
|
|
|
|
name: attachment.name.clone(),
|
|
|
|
value: property_value.to_string(),
|
|
|
|
value_source: None,
|
|
|
|
};
|
|
|
|
Ok(field)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-02-18 22:25:49 +00:00
|
|
|
use mitra_utils::{
|
2023-02-12 22:04:31 +00:00
|
|
|
caip2::ChainId,
|
|
|
|
};
|
2022-07-23 22:02:47 +00:00
|
|
|
use super::*;
|
|
|
|
|
2022-07-23 20:21:23 +00:00
|
|
|
const INSTANCE_URL: &str = "https://example.com";
|
|
|
|
|
2022-07-23 22:02:47 +00:00
|
|
|
#[test]
|
|
|
|
fn test_extra_field() {
|
|
|
|
let field = ExtraField {
|
|
|
|
name: "test".to_string(),
|
|
|
|
value: "value".to_string(),
|
|
|
|
value_source: None,
|
|
|
|
};
|
|
|
|
let attachment = attach_extra_field(field.clone());
|
|
|
|
assert_eq!(attachment.object_type, PROPERTY_VALUE);
|
2022-07-23 20:21:23 +00:00
|
|
|
|
2022-07-23 22:02:47 +00:00
|
|
|
let parsed_field = parse_extra_field(&attachment).unwrap();
|
|
|
|
assert_eq!(parsed_field.name, field.name);
|
|
|
|
assert_eq!(parsed_field.value, field.value);
|
|
|
|
}
|
2022-07-23 20:21:23 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_payment_option() {
|
2023-03-21 13:47:31 +00:00
|
|
|
let username = "testuser";
|
2022-08-29 19:17:16 +00:00
|
|
|
let payment_option =
|
|
|
|
PaymentOption::ethereum_subscription(ChainId::ethereum_mainnet());
|
2022-07-23 20:21:23 +00:00
|
|
|
let subscription_page_url =
|
2023-03-21 13:47:31 +00:00
|
|
|
"https://example.com/@testuser/subscription";
|
2022-07-23 20:21:23 +00:00
|
|
|
let attachment = attach_payment_option(
|
|
|
|
INSTANCE_URL,
|
2023-03-21 13:47:31 +00:00
|
|
|
username,
|
2022-07-23 20:21:23 +00:00
|
|
|
payment_option,
|
|
|
|
);
|
|
|
|
assert_eq!(attachment.object_type, LINK);
|
|
|
|
assert_eq!(attachment.name, "EthereumSubscription");
|
|
|
|
assert_eq!(attachment.href.as_deref().unwrap(), subscription_page_url);
|
|
|
|
|
|
|
|
let parsed_option = parse_payment_option(&attachment).unwrap();
|
2022-08-25 00:09:37 +00:00
|
|
|
let link = match parsed_option {
|
|
|
|
PaymentOption::Link(link) => link,
|
|
|
|
_ => panic!("wrong option"),
|
|
|
|
};
|
|
|
|
assert_eq!(link.name, "EthereumSubscription");
|
|
|
|
assert_eq!(link.href, subscription_page_url);
|
2022-07-23 20:21:23 +00:00
|
|
|
}
|
2022-07-23 22:02:47 +00:00
|
|
|
}
|