use {
crate::{ConnectTokenEncoder, Error, Result},
anyhow::Context,
base64::{engine::general_purpose::STANDARD as STANDARD_ENGINE, Engine},
serde::{Deserialize, Serialize},
std::{fs::Permissions, io::Write, path::Path},
};
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
#[cfg(unix)]
fn set_permissions_private(p: &mut Permissions) {
p.set_mode(0o600);
}
#[cfg(windows)]
fn set_permissions_private(_: &mut Permissions) {}
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UnifiedApiKey {
issuer_id: String,
key_id: String,
private_key: String,
}
impl UnifiedApiKey {
pub fn from_ecdsa_pem_path(
issuer_id: impl ToString,
key_id: impl ToString,
path: impl AsRef<Path>,
) -> Result<Self> {
let pem_data = std::fs::read(path.as_ref())?;
let parsed = pem::parse(pem_data).map_err(|_| InvalidPemPrivateKey)?;
if parsed.tag() != "PRIVATE KEY" {
return Err(InvalidPemPrivateKey.into());
}
let private_key = STANDARD_ENGINE.encode(parsed.contents());
Ok(Self {
issuer_id: issuer_id.to_string(),
key_id: key_id.to_string(),
private_key,
})
}
pub fn from_json(data: impl AsRef<[u8]>) -> Result<Self> {
Ok(serde_json::from_slice(data.as_ref())?)
}
pub fn from_json_path(path: impl AsRef<Path>) -> Result<Self> {
let data = std::fs::read(path.as_ref())?;
Self::from_json(data)
}
pub fn to_json_string(&self) -> Result<String> {
Ok(serde_json::to_string_pretty(&self)?)
}
pub fn write_json_file(&self, path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
let data = self.to_json_string()?;
let mut fh = std::fs::File::create(path)?;
let mut permissions = fh.metadata()?.permissions();
set_permissions_private(&mut permissions);
fh.set_permissions(permissions)?;
fh.write_all(data.as_bytes())?;
Ok(())
}
}
impl TryFrom<UnifiedApiKey> for ConnectTokenEncoder {
type Error = anyhow::Error;
fn try_from(value: UnifiedApiKey) -> Result<Self> {
let der = STANDARD_ENGINE
.decode(value.private_key)
.context("invalid unified api key")?;
Self::from_ecdsa_der(value.key_id, value.issuer_id, &der)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Error)]
#[error("invalid PEM formatted private key")]
pub struct InvalidPemPrivateKey;