mirror of
https://codeberg.org/tyy/aspm
synced 2024-12-23 00:09:28 -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"
|
name = "aspm"
|
||||||
authors = ["Ty"]
|
authors = ["Ty"]
|
||||||
description = "A tool to manage ariadne signature profiles, implementing v0 of the specification"
|
description = "A tool to manage ariadne signature profiles, implementing v0 of the specification"
|
||||||
|
repository = "https://codeberg.org/tyy/aspm"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
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"] }
|
tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] }
|
||||||
sea-orm-migration = "0.11.3"
|
sea-orm-migration = "0.11.3"
|
||||||
async-trait = "0.1.68"
|
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> {
|
pub fn generate(key_type: AspKeyType) -> Result<Self, AspKeyError> {
|
||||||
let result: anyhow::Result<Self> = try {
|
(|| -> anyhow::Result<Self> {
|
||||||
match key_type {
|
match key_type {
|
||||||
AspKeyType::Ed25519 => {
|
AspKeyType::Ed25519 => {
|
||||||
let jwk = Jwk::generate_ed_key(EdCurve::Ed25519)?;
|
let jwk = Jwk::generate_ed_key(EdCurve::Ed25519)?;
|
||||||
Self {
|
Ok(Self {
|
||||||
key_type,
|
key_type,
|
||||||
fingerprint: jwk.get_fingerprint()?,
|
fingerprint: jwk.get_fingerprint()?,
|
||||||
jwk,
|
jwk,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
AspKeyType::ES256 => {
|
AspKeyType::ES256 => {
|
||||||
let jwk = Jwk::generate_ec_key(EcCurve::P256)?;
|
let jwk = Jwk::generate_ec_key(EcCurve::P256)?;
|
||||||
Self {
|
Ok(Self {
|
||||||
key_type,
|
key_type,
|
||||||
fingerprint: jwk.get_fingerprint()?,
|
fingerprint: jwk.get_fingerprint()?,
|
||||||
jwk,
|
jwk,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
})().or(Err(AspKeyError::GenerationError))
|
||||||
result.or(Err(AspKeyError::GenerationError))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_signer(&self) -> anyhow::Result<Box<dyn JwsSigner>> {
|
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 keys;
|
||||||
pub mod profiles;
|
pub mod profiles;
|
||||||
pub mod utils;
|
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 serde_email::Email;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::utils::jwt::JwtSerializable;
|
use crate::utils::jwt::{AspJwsType, JwtSerializable};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub enum AspType {
|
|
||||||
Profile,
|
|
||||||
Request,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
pub struct AriadneSignatureProfile {
|
pub struct AriadneSignatureProfile {
|
||||||
#[serde(rename = "http://ariadne.id/version")]
|
#[serde(rename = "http://ariadne.id/version")]
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
#[serde(rename = "http://ariadne.id/type")]
|
#[serde(rename = "http://ariadne.id/type")]
|
||||||
pub r#type: AspType,
|
pub r#type: AspJwsType,
|
||||||
#[serde(rename = "http://ariadne.id/name")]
|
#[serde(rename = "http://ariadne.id/name")]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(rename = "http://ariadne.id/claims")]
|
#[serde(rename = "http://ariadne.id/claims")]
|
||||||
|
@ -53,30 +46,21 @@ mod tests {
|
||||||
use hex_color::HexColor;
|
use hex_color::HexColor;
|
||||||
use josekit::jwk::Jwk;
|
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]
|
#[test]
|
||||||
fn serializing_profile_succeeds() {
|
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(crate::test_constants::KEY).unwrap())
|
||||||
let key = TryInto::<AspKey>::try_into(
|
.unwrap();
|
||||||
Jwk::from_bytes(
|
|
||||||
r#"
|
|
||||||
{"kty":"EC",
|
|
||||||
"crv":"P-256",
|
|
||||||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
|
|
||||||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
|
|
||||||
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let profile = AriadneSignatureProfile {
|
let profile = AriadneSignatureProfile {
|
||||||
version: 0,
|
version: 0,
|
||||||
r#type: AspType::Profile,
|
r#type: AspJwsType::Profile,
|
||||||
name: "Example name".to_string(),
|
name: "Example name".to_string(),
|
||||||
claims: vec![
|
claims: vec![
|
||||||
"dns:example.com?type=TXT".to_string(),
|
"dns:example.com?type=TXT".to_string(),
|
||||||
|
@ -94,31 +78,18 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deserializing_profile_succeeds() {
|
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(crate::test_constants::KEY).unwrap())
|
||||||
let key = TryInto::<AspKey>::try_into(
|
.unwrap();
|
||||||
Jwk::from_bytes(
|
|
||||||
r#"
|
|
||||||
{"kty":"EC",
|
|
||||||
"crv":"P-256",
|
|
||||||
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
|
|
||||||
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
|
|
||||||
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let jwt = r"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicHJvZmlsZSIsImh0dHA6Ly9hcmlhZG5lLmlkL25hbWUiOiJFeGFtcGxlIG5hbWUiLCJodHRwOi8vYXJpYWRuZS5pZC9jbGFpbXMiOlsiZG5zOmV4YW1wbGUuY29tP3R5cGU9VFhUIiwiaHR0cHM6Ly9naXQuZXhhbXBsZS5jb20vZXhhbXBsZS9mb3JnZWpvX3Byb29mIl0sImh0dHA6Ly9hcmlhZG5lLmlkL2NvbG9yIjoiI0E0MzRFQiJ9.u5AbAqRpyXetXwU_QqpZrieNzwZGCRZ0tFTL4FoIwPRiZZ9iIGBnqs7PWbsd0iHQpYT_Q7s1GmwggGssM9ttxQ";
|
let profile =
|
||||||
|
AriadneSignatureProfile::decode_and_verify(crate::test_constants::PROFILE, &key);
|
||||||
let profile = AriadneSignatureProfile::decode_and_verify(jwt, &key);
|
|
||||||
assert!(profile.is_ok(), "Profile should parse and verify correctly");
|
assert!(profile.is_ok(), "Profile should parse and verify correctly");
|
||||||
let profile = profile.unwrap();
|
let profile = profile.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
*profile,
|
*profile,
|
||||||
AriadneSignatureProfile {
|
AriadneSignatureProfile {
|
||||||
version: 0,
|
version: 0,
|
||||||
r#type: AspType::Profile,
|
r#type: AspJwsType::Profile,
|
||||||
name: "Example name".to_string(),
|
name: "Example name".to_string(),
|
||||||
claims: vec![
|
claims: vec![
|
||||||
"dns:example.com?type=TXT".to_string(),
|
"dns:example.com?type=TXT".to_string(),
|
||||||
|
|
|
@ -8,6 +8,13 @@ use thiserror::Error;
|
||||||
|
|
||||||
use crate::keys::{AspKey, JwsHeaderExt};
|
use crate::keys::{AspKey, JwsHeaderExt};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum AspJwsType {
|
||||||
|
Profile,
|
||||||
|
Request,
|
||||||
|
}
|
||||||
|
|
||||||
pub trait JwtSerializable {}
|
pub trait JwtSerializable {}
|
||||||
|
|
||||||
pub trait JwtSerialize {
|
pub trait JwtSerialize {
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[toolchain]
|
|
||||||
channel = "nightly"
|
|
Loading…
Reference in a new issue