use crate::contracts::{
KIND_DELEGATION_TOKEN, KIND_REQUEST_ENVELOPE, SPEC_VERSION_CURRENT, validate_token,
};
use crate::crypto::{SIGNATURE_ALG_ED25519, sign_token};
use crate::models::{DelegationToken, RequestEnvelope, Violation};
use chrono::{DateTime, Utc};
use ed25519_dalek::SigningKey;
#[derive(Default)]
pub struct DelegationTokenBuilder {
token_id: Option<String>,
issuer: Option<String>,
agent_id: Option<String>,
delegator_id: Option<String>,
audience: Vec<String>,
allowed_actions: Vec<String>,
allowed_resources: Option<Vec<String>>,
max_delegation_depth: Option<u16>,
issued_at: Option<DateTime<Utc>>,
expires_at: Option<DateTime<Utc>>,
nonce: Option<String>,
key_id: Option<String>,
}
impl DelegationTokenBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn token_id(mut self, value: impl Into<String>) -> Self {
self.token_id = Some(value.into());
self
}
pub fn issuer(mut self, value: impl Into<String>) -> Self {
self.issuer = Some(value.into());
self
}
pub fn agent_id(mut self, value: impl Into<String>) -> Self {
self.agent_id = Some(value.into());
self
}
pub fn delegator_id(mut self, value: impl Into<String>) -> Self {
self.delegator_id = Some(value.into());
self
}
pub fn audience(mut self, value: impl Into<String>) -> Self {
self.audience.push(value.into());
self
}
pub fn allowed_action(mut self, value: impl Into<String>) -> Self {
self.allowed_actions.push(value.into());
self
}
pub fn allowed_resource(mut self, value: impl Into<String>) -> Self {
self.allowed_resources
.get_or_insert_with(Vec::new)
.push(value.into());
self
}
pub fn max_delegation_depth(mut self, value: u16) -> Self {
self.max_delegation_depth = Some(value);
self
}
pub fn issued_at(mut self, value: DateTime<Utc>) -> Self {
self.issued_at = Some(value);
self
}
pub fn expires_at(mut self, value: DateTime<Utc>) -> Self {
self.expires_at = Some(value);
self
}
pub fn nonce(mut self, value: impl Into<String>) -> Self {
self.nonce = Some(value.into());
self
}
pub fn key_id(mut self, value: impl Into<String>) -> Self {
self.key_id = Some(value.into());
self
}
pub fn build_and_sign(self, issuer_key: &SigningKey) -> Result<DelegationToken, Violation> {
let mut token = DelegationToken {
spec_version: SPEC_VERSION_CURRENT.to_owned(),
kind: KIND_DELEGATION_TOKEN.to_owned(),
token_id: required(self.token_id, "token_id")?,
issuer: required(self.issuer, "issuer")?,
agent_id: required(self.agent_id, "agent_id")?,
delegator_id: required(self.delegator_id, "delegator_id")?,
audience: self.audience,
allowed_actions: self.allowed_actions,
allowed_resources: self.allowed_resources,
max_delegation_depth: self.max_delegation_depth,
issued_at: required(self.issued_at, "issued_at")?,
expires_at: required(self.expires_at, "expires_at")?,
nonce: required(self.nonce, "nonce")?,
key_id: required(self.key_id, "key_id")?,
signature_alg: SIGNATURE_ALG_ED25519.to_owned(),
signature: String::new(),
};
token.signature = "pending".to_owned();
validate_token(&token)?;
token.signature.clear();
token.signature = sign_token(&token, issuer_key)?;
Ok(token)
}
}
pub fn envelope(token: DelegationToken, request_id: Option<String>) -> RequestEnvelope {
RequestEnvelope {
spec_version: SPEC_VERSION_CURRENT.to_owned(),
kind: KIND_REQUEST_ENVELOPE.to_owned(),
request_id,
delegation_token: token,
}
}
fn required<T>(value: Option<T>, field: &str) -> Result<T, Violation> {
value.ok_or_else(|| Violation::new("issuance", format!("{field} is required")))
}