use crate::credentials::service_account::ServiceAccountKey;
use crate::signer::{Result, SigningError, dynamic::SigningProvider};
#[derive(Clone, Debug)]
pub(crate) struct ServiceAccountSigner {
service_account_key: ServiceAccountKey,
client_email: String,
}
impl ServiceAccountSigner {
pub(crate) fn new(service_account_key: ServiceAccountKey) -> Self {
Self {
service_account_key: service_account_key.clone(),
client_email: service_account_key.client_email.clone(),
}
}
pub(crate) fn from_impersonated_service_account(
service_account_key: ServiceAccountKey,
client_email: String,
) -> Self {
Self {
service_account_key,
client_email,
}
}
}
#[async_trait::async_trait]
impl SigningProvider for ServiceAccountSigner {
async fn client_email(&self) -> Result<String> {
Ok(self.client_email.clone())
}
async fn sign(&self, content: &[u8]) -> Result<bytes::Bytes> {
let signer = self
.service_account_key
.signer()
.map_err(SigningError::parsing)?;
let signature = signer.sign(content).map_err(SigningError::sign)?;
Ok(bytes::Bytes::from(signature))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::credentials::tests::PKCS8_PK;
use serde_json::{Value, json};
type TestResult = anyhow::Result<()>;
fn get_mock_service_key() -> Value {
json!({
"client_email": "test-client-email",
"private_key_id": "test-private-key-id",
"private_key": "",
"project_id": "test-project-id",
})
}
#[tokio::test]
async fn test_service_account_signer_success() -> TestResult {
let mut service_account_key = get_mock_service_key();
service_account_key["private_key"] = Value::from(PKCS8_PK.clone());
let service_account_key =
serde_json::from_value::<ServiceAccountKey>(service_account_key.clone())?;
let signer = ServiceAccountSigner::new(service_account_key.clone());
let client_email = signer.client_email().await?;
assert_eq!(client_email, service_account_key.client_email);
let result = signer.sign(b"test").await?;
let inner_signer = service_account_key.signer().unwrap();
let inner_result = inner_signer.sign(b"test")?;
assert_eq!(result.as_ref(), inner_result);
Ok(())
}
#[tokio::test]
async fn test_service_account_impersonated_signer_success() -> TestResult {
let mut service_account_key = get_mock_service_key();
service_account_key["private_key"] = Value::from(PKCS8_PK.clone());
let service_account_key =
serde_json::from_value::<ServiceAccountKey>(service_account_key.clone())?;
let signer = ServiceAccountSigner::from_impersonated_service_account(
service_account_key.clone(),
"impersonated-client-email".to_string(),
);
let client_email = signer.client_email().await?;
assert_eq!(client_email, "impersonated-client-email");
let result = signer.sign(b"test").await?;
let inner_signer = service_account_key.signer().unwrap();
let inner_result = inner_signer.sign(b"test")?;
assert_eq!(result.as_ref(), inner_result);
Ok(())
}
}