use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use crate::schema::WebcPackageIdentifierV1;
use super::DeploymentV1;
use uuid::Uuid;
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct BaseClaims {
#[serde(with = "time::serde::timestamp")]
pub exp: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
pub iat: OffsetDateTime,
pub sub: String,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, schemars::JsonSchema)]
pub struct DeployWorkloadTokenV1 {
#[serde(with = "time::serde::timestamp")]
#[schemars(with = "u64")]
pub exp: OffsetDateTime,
#[serde(with = "time::serde::timestamp")]
#[schemars(with = "u64")]
pub iat: OffsetDateTime,
pub sub: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub jti: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cfg: Option<DeploymentConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub deployment: Option<DeploymentV1>,
#[serde(skip_serializing_if = "Option::is_none")]
pub webc: Option<WebcPackageIdentifierV1>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allowed_packages: Option<Vec<WebcPackageIdentifierV1>>,
}
impl DeployWorkloadTokenV1 {
pub fn network_id(&self) -> Option<Uuid> {
self.deployment
.iter()
.flat_map(|d| d.workload.capabilities.network.as_ref())
.flat_map(|n| n.network_id)
.next()
}
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, schemars::JsonSchema)]
pub struct DeploymentConfig {
pub backend_url: url::Url,
pub uid: String,
pub backend_app_version_id: Option<String>,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
#[serde(untagged)]
pub enum DeployWorkloadTokenData {
V1(DeployWorkloadTokenV1),
}
impl From<DeployWorkloadTokenV1> for DeployWorkloadTokenData {
fn from(value: DeployWorkloadTokenV1) -> Self {
Self::V1(value)
}
}
#[derive(PartialEq, Eq, Clone, Debug, Serialize)]
pub struct DeployWorkloadToken {
pub raw: String,
pub data: DeployWorkloadTokenData,
}
impl DeployWorkloadTokenData {
pub fn expires(&self) -> Option<&OffsetDateTime> {
match self {
DeployWorkloadTokenData::V1(v1) => Some(&v1.exp),
}
}
pub fn issued_at(&self) -> &OffsetDateTime {
match self {
DeployWorkloadTokenData::V1(v1) => &v1.iat,
}
}
pub fn subject(&self) -> &str {
match self {
DeployWorkloadTokenData::V1(v1) => &v1.sub,
}
}
pub fn webc_spec(&self) -> Option<&WebcPackageIdentifierV1> {
match self {
Self::V1(v1) => v1.webc.as_ref(),
}
}
pub fn deployment_config(&self) -> Option<&DeploymentConfig> {
match self {
Self::V1(v) => v.cfg.as_ref(),
}
}
pub fn jti(&self) -> Option<&Uuid> {
match self {
Self::V1(v) => v.jti.as_ref(),
}
}
pub fn has_webc_spec(&self) -> bool {
self.webc_spec().is_some()
}
pub fn network_id(&self) -> Option<Uuid> {
match self {
DeployWorkloadTokenData::V1(v1) => v1.network_id(),
}
}
pub fn as_v1(&self) -> Option<&DeployWorkloadTokenV1> {
match &self {
DeployWorkloadTokenData::V1(x) => Some(x),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deser_tokens() {
#[allow(deprecated)]
let expected = DeployWorkloadTokenData::V1(DeployWorkloadTokenV1 {
exp: OffsetDateTime::from_unix_timestamp(10000).unwrap(),
iat: OffsetDateTime::from_unix_timestamp(20000).unwrap(),
sub: "user-1".to_string(),
jti: None,
deployment: None,
cfg: Some(DeploymentConfig {
backend_url: "http://test.com".parse().unwrap(),
uid: "123".to_string(),
backend_app_version_id: None,
}),
webc: Some(WebcPackageIdentifierV1 {
repository: Some("https://registry.wapm.dev".parse().unwrap()),
namespace: "ns".to_string(),
name: "name".to_string(),
tag: Some("1.2.3".to_string()),
}),
allowed_packages: Some(vec![WebcPackageIdentifierV1 {
repository: Some("https://registry.wapm.dev".parse().unwrap()),
namespace: "ns".to_string(),
name: "name".to_string(),
tag: Some("1.2.3".to_string()),
}]),
});
let raw = r#"
{
"exp": 10000,
"iat": 20000,
"sub": "user-1",
"webc": {
"repository": "https://registry.wapm.dev/",
"namespace": "ns",
"name": "name",
"tag": "1.2.3",
"hash": null,
"download_url": "https://test.com/lala"
},
"cfg": {
"uid": "123",
"backend_url": "http://test.com"
},
"allowed_packages": [
{
"repository": "https://registry.wapm.dev/",
"namespace": "ns",
"name": "name",
"tag": "1.2.3",
"hash": null,
"download_url": "https://test.com/lala"
}
]
}
"#;
let deser: DeployWorkloadTokenData =
crate::schema::deserialize_json(raw.as_bytes()).unwrap();
assert_eq!(
deser
.webc_spec()
.unwrap()
.build_download_url()
.unwrap()
.to_string(),
"https://registry.wapm.dev/ns/name@1.2.3",
);
pretty_assertions::assert_eq!(deser, expected);
}
}