mirror of
https://codeberg.org/tyy/aspm
synced 2024-12-22 15:59:29 -07:00
Support aspe servers in crates/asp, and remove nightly requirement
This commit is contained in:
parent
19c42f6138
commit
57e73d62dd
8 changed files with 371 additions and 55 deletions
|
@ -2,6 +2,7 @@
|
|||
name = "aspm"
|
||||
authors = ["Ty"]
|
||||
description = "A tool to manage ariadne signature profiles, implementing v0 of the specification"
|
||||
repository = "https://codeberg.org/tyy/aspm"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
@ -25,3 +26,6 @@ sea-orm = { version = "0.11.3", features = ["sqlx-sqlite", "runtime-tokio-native
|
|||
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
|
||||
sea-orm-migration = "0.11.3"
|
||||
async-trait = "0.1.68"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
|
136
crates/asp/src/aspe/mod.rs
Normal file
136
crates/asp/src/aspe/mod.rs
Normal file
|
@ -0,0 +1,136 @@
|
|||
pub mod requests;
|
||||
|
||||
use reqwest::{
|
||||
header::{self, HeaderValue},
|
||||
StatusCode,
|
||||
};
|
||||
pub use url::Host;
|
||||
|
||||
/// An ASPE-compatible server
|
||||
pub struct AspeServer {
|
||||
host: Host,
|
||||
client: reqwest::Client,
|
||||
}
|
||||
|
||||
pub enum AspeRequestFailure {
|
||||
/// Signals that the request failed because of invalid data being provided
|
||||
BadRequest,
|
||||
/// Signals that the request failed because the provided data was too large
|
||||
TooLarge,
|
||||
/// Signals that the request failed because of a rate-limit
|
||||
RateLimited,
|
||||
/// Signals that the request failed for some unknown reason
|
||||
Unknown(reqwest::Error),
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for AspeRequestFailure {
|
||||
fn from(value: reqwest::Error) -> Self {
|
||||
if !value.is_status() {
|
||||
return Self::Unknown(value);
|
||||
} else {
|
||||
match value.status().unwrap() {
|
||||
StatusCode::BAD_REQUEST => Self::BadRequest,
|
||||
StatusCode::PAYLOAD_TOO_LARGE => Self::TooLarge,
|
||||
StatusCode::TOO_MANY_REQUESTS => Self::RateLimited,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum AspeFetchFailure {
|
||||
/// Signals that the request failed because the requested profile did not exist
|
||||
NotFound,
|
||||
/// Signals that the request failed because the provided data was too large
|
||||
TooLarge,
|
||||
/// Signals that the request failed because of a rate-limit
|
||||
RateLimited,
|
||||
/// Signals that the request failed for some unknown reason
|
||||
Unknown(reqwest::Error),
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for AspeFetchFailure {
|
||||
fn from(value: reqwest::Error) -> Self {
|
||||
if !value.is_status() {
|
||||
return Self::Unknown(value);
|
||||
} else {
|
||||
match value.status().unwrap() {
|
||||
StatusCode::NOT_FOUND => Self::NotFound,
|
||||
StatusCode::PAYLOAD_TOO_LARGE => Self::TooLarge,
|
||||
StatusCode::TOO_MANY_REQUESTS => Self::RateLimited,
|
||||
_ => Self::Unknown(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AspeServer {
|
||||
/// Creates a new [AspeServer] instance given the specified domain. A domain and ONLY a domain should be provided (no scheme, no path, etc).
|
||||
pub fn new(host: Host) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
host,
|
||||
client: reqwest::Client::builder()
|
||||
.user_agent(format!(
|
||||
"asp-rs/{version} ({repo})",
|
||||
version = env!("CARGO_PKG_VERSION"),
|
||||
repo = env!("CARGO_PKG_REPOSITORY")
|
||||
))
|
||||
.https_only(true)
|
||||
.build()?,
|
||||
})
|
||||
}
|
||||
|
||||
/// POSTs a request JWS to the aspe server. This will return a result with the [Err] variant containing an enum detailing specifically why the request failed.
|
||||
pub async fn post_request(&self, jws: impl Into<String>) -> Result<(), AspeRequestFailure> {
|
||||
self.client
|
||||
.post(format!(
|
||||
"https://{host}/.well-known/aspe/post/",
|
||||
host = self.host
|
||||
))
|
||||
.header(
|
||||
header::CONTENT_TYPE,
|
||||
HeaderValue::from_static("application/asp+jwt; charset=UTF-8"),
|
||||
)
|
||||
.body(jws.into())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fetch_profile(
|
||||
&self,
|
||||
fingerprint: impl Into<String>,
|
||||
) -> Result<String, AspeFetchFailure> {
|
||||
let res = self
|
||||
.client
|
||||
.get(format!(
|
||||
"https://{host}/.well-known/aspe/id/{fingerprint}",
|
||||
host = self.host,
|
||||
fingerprint = fingerprint.into()
|
||||
))
|
||||
.header(
|
||||
header::ACCEPT,
|
||||
HeaderValue::from_static("application/asp+jwt; charset=UTF-8"),
|
||||
)
|
||||
.send()
|
||||
.await?
|
||||
.text_with_charset("utf-8")
|
||||
.await?;
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
// TODO: After an implementation of an ASPE server is created, mock it for testing
|
||||
|
||||
use super::{AspeServer, Host};
|
||||
|
||||
#[test]
|
||||
fn building_aspe_server_succeeds() {
|
||||
let result = AspeServer::new(Host::Domain(String::from("example.com")));
|
||||
assert!(result.is_ok(), "Constructing an AspeServer should succeed")
|
||||
}
|
||||
}
|
184
crates/asp/src/aspe/requests.rs
Normal file
184
crates/asp/src/aspe/requests.rs
Normal file
|
@ -0,0 +1,184 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::utils::jwt::JwtSerializable;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AspeRequestType {
|
||||
Request,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[serde(tag = "http://ariadne.id/action", rename_all = "camelCase")]
|
||||
pub enum AspeRequestVariant {
|
||||
Create {
|
||||
#[serde(rename = "http://ariadne.id/profile_jws")]
|
||||
profile_jws: String,
|
||||
},
|
||||
Update {
|
||||
#[serde(rename = "http://ariadne.id/profile_jws")]
|
||||
profile_jws: String,
|
||||
#[serde(rename = "http://ariadne.id/aspe_uri")]
|
||||
aspe_uri: String,
|
||||
},
|
||||
Delete {
|
||||
#[serde(rename = "http://ariadne.id/aspe_uri")]
|
||||
aspe_uri: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub struct AspeRequest {
|
||||
#[serde(rename = "http://ariadne.id/version")]
|
||||
pub version: u8,
|
||||
#[serde(rename = "http://ariadne.id/type")]
|
||||
pub r#type: AspeRequestType,
|
||||
#[serde(flatten)]
|
||||
pub request: AspeRequestVariant,
|
||||
}
|
||||
|
||||
impl JwtSerializable for AspeRequest {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use josekit::jwk::Jwk;
|
||||
|
||||
use crate::{keys::AspKey, utils::jwt::JwtSerialize};
|
||||
|
||||
use super::{AspeRequest, AspeRequestType, AspeRequestVariant};
|
||||
|
||||
#[test]
|
||||
fn serialize_create_request_jwt() {
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
let profile = crate::test_constants::PROFILE;
|
||||
|
||||
let request = AspeRequest {
|
||||
version: 0,
|
||||
r#type: AspeRequestType::Request,
|
||||
request: AspeRequestVariant::Create {
|
||||
profile_jws: profile.to_string(),
|
||||
},
|
||||
};
|
||||
let encoded = request.encode_and_sign(&key);
|
||||
assert!(
|
||||
encoded.is_ok(),
|
||||
"ASPE request JWS should sign and encode successfully"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_create_request_jwt() {
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let decoded = AspeRequest::decode_and_verify(crate::test_constants::CREATE_REQUEST, &key);
|
||||
assert!(
|
||||
decoded.is_ok(),
|
||||
"ASPE request JWS should verify and decode successfully"
|
||||
);
|
||||
let decoded = decoded.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*decoded,
|
||||
AspeRequest {
|
||||
version: 0,
|
||||
r#type: AspeRequestType::Request,
|
||||
request: AspeRequestVariant::Create {
|
||||
profile_jws: crate::test_constants::PROFILE.to_string(),
|
||||
},
|
||||
},
|
||||
"Decoded create request should be correct"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_update_request_jwt() {
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let request = AspeRequest {
|
||||
version: 0,
|
||||
r#type: AspeRequestType::Request,
|
||||
request: AspeRequestVariant::Update {
|
||||
profile_jws: crate::test_constants::PROFILE.to_string(),
|
||||
aspe_uri: crate::test_constants::ASPE_URI.to_string(),
|
||||
},
|
||||
};
|
||||
let encoded = dbg!(request.encode_and_sign(&key));
|
||||
assert!(
|
||||
encoded.is_ok(),
|
||||
"ASPE request JWS should sign and encode successfully"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_update_request_jwt() {
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let decoded = AspeRequest::decode_and_verify(crate::test_constants::UPDATE_REQUEST, &key);
|
||||
assert!(
|
||||
decoded.is_ok(),
|
||||
"ASPE request JWS should verify and decode successfully"
|
||||
);
|
||||
let decoded = decoded.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*decoded,
|
||||
AspeRequest {
|
||||
version: 0,
|
||||
r#type: AspeRequestType::Request,
|
||||
request: AspeRequestVariant::Update {
|
||||
profile_jws: crate::test_constants::PROFILE.to_string(),
|
||||
aspe_uri: crate::test_constants::ASPE_URI.to_string(),
|
||||
},
|
||||
},
|
||||
"Decoded update request should be correct"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_delete_request_jwt() {
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let request = AspeRequest {
|
||||
version: 0,
|
||||
r#type: AspeRequestType::Request,
|
||||
request: AspeRequestVariant::Delete {
|
||||
aspe_uri: crate::test_constants::ASPE_URI.to_string(),
|
||||
},
|
||||
};
|
||||
let encoded = dbg!(request.encode_and_sign(&key));
|
||||
assert!(
|
||||
encoded.is_ok(),
|
||||
"ASPE request JWS should sign and encode successfully"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_delete_request_jwt() {
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let decoded = AspeRequest::decode_and_verify(crate::test_constants::DELETE_REQUEST, &key);
|
||||
assert!(
|
||||
decoded.is_ok(),
|
||||
"ASPE request JWS should verify and decode successfully"
|
||||
);
|
||||
let decoded = decoded.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
*decoded,
|
||||
AspeRequest {
|
||||
version: 0,
|
||||
r#type: AspeRequestType::Request,
|
||||
request: AspeRequestVariant::Delete {
|
||||
aspe_uri: crate::test_constants::ASPE_URI.to_string(),
|
||||
},
|
||||
},
|
||||
"Decoded delete request should be correct"
|
||||
)
|
||||
}
|
||||
}
|
|
@ -86,27 +86,26 @@ impl AspKey {
|
|||
}
|
||||
|
||||
pub fn generate(key_type: AspKeyType) -> Result<Self, AspKeyError> {
|
||||
let result: anyhow::Result<Self> = try {
|
||||
(|| -> anyhow::Result<Self> {
|
||||
match key_type {
|
||||
AspKeyType::Ed25519 => {
|
||||
let jwk = Jwk::generate_ed_key(EdCurve::Ed25519)?;
|
||||
Self {
|
||||
Ok(Self {
|
||||
key_type,
|
||||
fingerprint: jwk.get_fingerprint()?,
|
||||
jwk,
|
||||
}
|
||||
})
|
||||
}
|
||||
AspKeyType::ES256 => {
|
||||
let jwk = Jwk::generate_ec_key(EcCurve::P256)?;
|
||||
Self {
|
||||
Ok(Self {
|
||||
key_type,
|
||||
fingerprint: jwk.get_fingerprint()?,
|
||||
jwk,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
result.or(Err(AspKeyError::GenerationError))
|
||||
})().or(Err(AspKeyError::GenerationError))
|
||||
}
|
||||
|
||||
pub fn create_signer(&self) -> anyhow::Result<Box<dyn JwsSigner>> {
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
#![feature(try_blocks)]
|
||||
|
||||
pub mod aspe;
|
||||
pub mod keys;
|
||||
pub mod profiles;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test_constants {
|
||||
// NOTE: This key is taken from the example keys in RFC 7517
|
||||
pub(crate) static KEY: &'static str = r#"
|
||||
{"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
|
||||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
|
||||
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
|
||||
"#;
|
||||
pub(crate) static ASPE_URI: &'static str = "aspe:example.com:452JFAI6B3KOLKBAUX3MC73DAU";
|
||||
|
||||
pub(crate) static PROFILE: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicHJvZmlsZSIsImh0dHA6Ly9hcmlhZG5lLmlkL25hbWUiOiJFeGFtcGxlIG5hbWUiLCJodHRwOi8vYXJpYWRuZS5pZC9jbGFpbXMiOlsiZG5zOmV4YW1wbGUuY29tP3R5cGU9VFhUIiwiaHR0cHM6Ly9naXQuZXhhbXBsZS5jb20vZXhhbXBsZS9mb3JnZWpvX3Byb29mIl0sImh0dHA6Ly9hcmlhZG5lLmlkL2NvbG9yIjoiI0E0MzRFQiJ9.u5AbAqRpyXetXwU_QqpZrieNzwZGCRZ0tFTL4FoIwPRiZZ9iIGBnqs7PWbsd0iHQpYT_Q7s1GmwggGssM9ttxQ";
|
||||
pub(crate) static CREATE_REQUEST: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicmVxdWVzdCIsImh0dHA6Ly9hcmlhZG5lLmlkL2FjdGlvbiI6ImNyZWF0ZSIsImh0dHA6Ly9hcmlhZG5lLmlkL3Byb2ZpbGVfandzIjoiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalExTWtwR1FVazJRak5MVDB4TFFrRlZXRE5OUXpjelJFRlZJaXdpYW5kcklqcDdJbXQwZVNJNklrVkRJaXdpWTNKMklqb2lVQzB5TlRZaUxDSjRJam9pVFV0Q1ExUk9TV05MVlZORWFXa3hNWGxUY3pNMU1qWnBSRm80UVdsVWJ6ZFVkVFpMVUVGeGRqZEVOQ0lzSW5raU9pSTBSWFJzTmxOU1Z6SlphVXhWY2s0MWRtWjJWa2gxYUhBM2VEaFFlR3gwYlZkWGJHSmlUVFJKUm5sTkluMTkuZXlKb2RIUndPaTh2WVhKcFlXUnVaUzVwWkM5MlpYSnphVzl1SWpvd0xDSm9kSFJ3T2k4dllYSnBZV1J1WlM1cFpDOTBlWEJsSWpvaWNISnZabWxzWlNJc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyNWhiV1VpT2lKRmVHRnRjR3hsSUc1aGJXVWlMQ0pvZEhSd09pOHZZWEpwWVdSdVpTNXBaQzlqYkdGcGJYTWlPbHNpWkc1ek9tVjRZVzF3YkdVdVkyOXRQM1I1Y0dVOVZGaFVJaXdpYUhSMGNITTZMeTluYVhRdVpYaGhiWEJzWlM1amIyMHZaWGhoYlhCc1pTOW1iM0puWldwdlgzQnliMjltSWwwc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyTnZiRzl5SWpvaUkwRTBNelJGUWlKOS51NUFiQXFScHlYZXRYd1VfUXFwWnJpZU56d1pHQ1JaMHRGVEw0Rm9Jd1BSaVpaOWlJR0JucXM3UFdic2QwaUhRcFlUX1E3czFHbXdnZ0dzc005dHR4USJ9.f8NdVzrjCZKT2R5MzUZkgcnNIJWo6ftQj6MCvXF5cgpjYt3suTqOGoBs6EKvtsgVGs12uS4ZxNnVAnFMsKKGlQ";
|
||||
pub(crate) static UPDATE_REQUEST: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicmVxdWVzdCIsImh0dHA6Ly9hcmlhZG5lLmlkL2FjdGlvbiI6InVwZGF0ZSIsImh0dHA6Ly9hcmlhZG5lLmlkL3Byb2ZpbGVfandzIjoiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalExTWtwR1FVazJRak5MVDB4TFFrRlZXRE5OUXpjelJFRlZJaXdpYW5kcklqcDdJbXQwZVNJNklrVkRJaXdpWTNKMklqb2lVQzB5TlRZaUxDSjRJam9pVFV0Q1ExUk9TV05MVlZORWFXa3hNWGxUY3pNMU1qWnBSRm80UVdsVWJ6ZFVkVFpMVUVGeGRqZEVOQ0lzSW5raU9pSTBSWFJzTmxOU1Z6SlphVXhWY2s0MWRtWjJWa2gxYUhBM2VEaFFlR3gwYlZkWGJHSmlUVFJKUm5sTkluMTkuZXlKb2RIUndPaTh2WVhKcFlXUnVaUzVwWkM5MlpYSnphVzl1SWpvd0xDSm9kSFJ3T2k4dllYSnBZV1J1WlM1cFpDOTBlWEJsSWpvaWNISnZabWxzWlNJc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyNWhiV1VpT2lKRmVHRnRjR3hsSUc1aGJXVWlMQ0pvZEhSd09pOHZZWEpwWVdSdVpTNXBaQzlqYkdGcGJYTWlPbHNpWkc1ek9tVjRZVzF3YkdVdVkyOXRQM1I1Y0dVOVZGaFVJaXdpYUhSMGNITTZMeTluYVhRdVpYaGhiWEJzWlM1amIyMHZaWGhoYlhCc1pTOW1iM0puWldwdlgzQnliMjltSWwwc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyTnZiRzl5SWpvaUkwRTBNelJGUWlKOS51NUFiQXFScHlYZXRYd1VfUXFwWnJpZU56d1pHQ1JaMHRGVEw0Rm9Jd1BSaVpaOWlJR0JucXM3UFdic2QwaUhRcFlUX1E3czFHbXdnZ0dzc005dHR4USIsImh0dHA6Ly9hcmlhZG5lLmlkL2FzcGVfdXJpIjoiYXNwZTpleGFtcGxlLmNvbTo0NTJKRkFJNkIzS09MS0JBVVgzTUM3M0RBVSJ9.044vzbhefes8bFJFrXLwU2RNYhNvK_rNDDqM7NjEaC8alyFl-5Fh_Obj-pIKUkcxD-HL27y2objt_-lbDqvw4g";
|
||||
pub(crate) static DELETE_REQUEST: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicmVxdWVzdCIsImh0dHA6Ly9hcmlhZG5lLmlkL2FjdGlvbiI6ImRlbGV0ZSIsImh0dHA6Ly9hcmlhZG5lLmlkL2FzcGVfdXJpIjoiYXNwZTpleGFtcGxlLmNvbTo0NTJKRkFJNkIzS09MS0JBVVgzTUM3M0RBVSJ9.DJNuN-wTXxOW3VZHcN_tlUIFOHfI0GeD_uzs1RplwsGTBe0Z4KpIojEQ85N7tSnuLxuGlsR8kd1SrbcvxhkWaw";
|
||||
}
|
||||
|
|
|
@ -3,21 +3,14 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_email::Email;
|
||||
use url::Url;
|
||||
|
||||
use crate::utils::jwt::JwtSerializable;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AspType {
|
||||
Profile,
|
||||
Request,
|
||||
}
|
||||
use crate::utils::jwt::{AspJwsType, JwtSerializable};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
pub struct AriadneSignatureProfile {
|
||||
#[serde(rename = "http://ariadne.id/version")]
|
||||
pub version: u8,
|
||||
#[serde(rename = "http://ariadne.id/type")]
|
||||
pub r#type: AspType,
|
||||
pub r#type: AspJwsType,
|
||||
#[serde(rename = "http://ariadne.id/name")]
|
||||
pub name: String,
|
||||
#[serde(rename = "http://ariadne.id/claims")]
|
||||
|
@ -53,30 +46,21 @@ mod tests {
|
|||
use hex_color::HexColor;
|
||||
use josekit::jwk::Jwk;
|
||||
|
||||
use crate::{keys::AspKey, utils::jwt::JwtSerialize};
|
||||
use crate::{
|
||||
keys::AspKey,
|
||||
utils::jwt::{AspJwsType, JwtSerialize},
|
||||
};
|
||||
|
||||
use super::{AriadneSignatureProfile, AspType};
|
||||
use super::AriadneSignatureProfile;
|
||||
|
||||
#[test]
|
||||
fn serializing_profile_succeeds() {
|
||||
// NOTE: This key is taken from the example keys in RFC 7517
|
||||
let key = TryInto::<AspKey>::try_into(
|
||||
Jwk::from_bytes(
|
||||
r#"
|
||||
{"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
|
||||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
|
||||
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let profile = AriadneSignatureProfile {
|
||||
version: 0,
|
||||
r#type: AspType::Profile,
|
||||
r#type: AspJwsType::Profile,
|
||||
name: "Example name".to_string(),
|
||||
claims: vec![
|
||||
"dns:example.com?type=TXT".to_string(),
|
||||
|
@ -94,31 +78,18 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn deserializing_profile_succeeds() {
|
||||
// NOTE: This key is taken from the example keys in RFC 7517
|
||||
let key = TryInto::<AspKey>::try_into(
|
||||
Jwk::from_bytes(
|
||||
r#"
|
||||
{"kty":"EC",
|
||||
"crv":"P-256",
|
||||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
|
||||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
|
||||
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
|
||||
"#,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let key = TryInto::<AspKey>::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap())
|
||||
.unwrap();
|
||||
|
||||
let jwt = r"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicHJvZmlsZSIsImh0dHA6Ly9hcmlhZG5lLmlkL25hbWUiOiJFeGFtcGxlIG5hbWUiLCJodHRwOi8vYXJpYWRuZS5pZC9jbGFpbXMiOlsiZG5zOmV4YW1wbGUuY29tP3R5cGU9VFhUIiwiaHR0cHM6Ly9naXQuZXhhbXBsZS5jb20vZXhhbXBsZS9mb3JnZWpvX3Byb29mIl0sImh0dHA6Ly9hcmlhZG5lLmlkL2NvbG9yIjoiI0E0MzRFQiJ9.u5AbAqRpyXetXwU_QqpZrieNzwZGCRZ0tFTL4FoIwPRiZZ9iIGBnqs7PWbsd0iHQpYT_Q7s1GmwggGssM9ttxQ";
|
||||
|
||||
let profile = AriadneSignatureProfile::decode_and_verify(jwt, &key);
|
||||
let profile =
|
||||
AriadneSignatureProfile::decode_and_verify(crate::test_constants::PROFILE, &key);
|
||||
assert!(profile.is_ok(), "Profile should parse and verify correctly");
|
||||
let profile = profile.unwrap();
|
||||
assert_eq!(
|
||||
*profile,
|
||||
AriadneSignatureProfile {
|
||||
version: 0,
|
||||
r#type: AspType::Profile,
|
||||
r#type: AspJwsType::Profile,
|
||||
name: "Example name".to_string(),
|
||||
claims: vec![
|
||||
"dns:example.com?type=TXT".to_string(),
|
||||
|
|
|
@ -8,6 +8,13 @@ use thiserror::Error;
|
|||
|
||||
use crate::keys::{AspKey, JwsHeaderExt};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AspJwsType {
|
||||
Profile,
|
||||
Request,
|
||||
}
|
||||
|
||||
pub trait JwtSerializable {}
|
||||
|
||||
pub trait JwtSerialize {
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[toolchain]
|
||||
channel = "nightly"
|
Loading…
Reference in a new issue