From cecc05e39936bfa8fd5457371769c46b7f30e7da Mon Sep 17 00:00:00 2001 From: Toru Ogawa Date: Thu, 23 May 2024 16:59:17 +0900 Subject: [PATCH] support url-sourced external account fix fix protocol --- src/external_account.rs | 59 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/external_account.rs b/src/external_account.rs index 90e7481f..241679db 100644 --- a/src/external_account.rs +++ b/src/external_account.rs @@ -11,6 +11,7 @@ use http::Uri; use hyper::client::connect::Connection; use hyper::header; use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::error::Error as StdError; use tokio::io::{AsyncRead, AsyncWrite}; use tower_service::Service; @@ -47,10 +48,33 @@ pub enum CredentialSource { /// file file: String, }, - // TODO: Microsoft Azure and URL-sourced credentials + /// Microsoft Azure and URL-sourced credentials + Url { + /// url + url: String, + /// headers + headers: Option>, + /// format + format: UrlCredentialSourceFormat, + }, // TODO: executable-sourced credentials } +/// JSON schema of URL-sourced credentials' format. +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(tag = "type")] +pub enum UrlCredentialSourceFormat { + /// text + #[serde(rename = "text")] + Text, + /// json + #[serde(rename = "json")] + Json { + /// subject_token_field_name + subject_token_field_name: String, + }, +} + /// ExternalAccountFlow can fetch oauth tokens using an external account secret. pub struct ExternalAccountFlow { pub(crate) secret: ExternalAccountSecret, @@ -72,6 +96,39 @@ impl ExternalAccountFlow { { let subject_token = match &self.secret.credential_source { CredentialSource::File { file } => tokio::fs::read_to_string(file).await?, + CredentialSource::Url { + url, + headers, + format, + } => { + let request = headers + .iter() + .flatten() + .fold(hyper::Request::get(url), |builder, (name, value)| { + builder.header(name, value) + }) + .body(hyper::Body::empty()) + .unwrap(); + + log::debug!("requesting credential from url: {:?}", request); + let (head, body) = hyper_client.request(request).await?.into_parts(); + let body = hyper::body::to_bytes(body).await?; + log::debug!("received response; head: {:?}, body: {:?}", head, body); + + match format { + UrlCredentialSourceFormat::Text => { + String::from_utf8(body.to_vec()).map_err(anyhow::Error::from)? + } + UrlCredentialSourceFormat::Json { + subject_token_field_name, + } => serde_json::from_slice::>(&body)? + .remove(subject_token_field_name) + .ok_or_else(|| anyhow::format_err!("missing {subject_token_field_name}"))? + .as_str() + .ok_or_else(|| anyhow::format_err!("invalid type"))? + .to_string(), + } + } }; let req = form_urlencoded::Serializer::new(String::new())