121 lines
3.4 KiB
Rust
121 lines
3.4 KiB
Rust
use serde::{Serialize, Deserialize};
|
|
use sqlx::{
|
|
FromRow,
|
|
Sqlite,
|
|
SqlitePool,
|
|
sqlite::SqliteRow,
|
|
Transaction,
|
|
types::Uuid,
|
|
};
|
|
use tokio_stream::StreamExt;
|
|
|
|
use crate::errors::*;
|
|
|
|
mod aws;
|
|
pub use aws::{AwsBaseCredential, AwsSessionCredential};
|
|
|
|
mod crypto;
|
|
pub use crypto::Crypto;
|
|
|
|
mod record;
|
|
pub use record::CredentialRecord;
|
|
|
|
mod session;
|
|
pub use session::AppSession;
|
|
|
|
mod ssh;
|
|
pub use ssh::SshKey;
|
|
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
|
#[serde(tag = "type")]
|
|
pub enum Credential {
|
|
AwsBase(AwsBaseCredential),
|
|
AwsSession(AwsSessionCredential),
|
|
Ssh(SshKey),
|
|
}
|
|
|
|
|
|
pub trait PersistentCredential: for<'a> Deserialize<'a> + Sized {
|
|
type Row: Send + Unpin + for<'r> FromRow<'r, SqliteRow>;
|
|
|
|
fn type_name() -> &'static str;
|
|
|
|
fn into_credential(self) -> Credential;
|
|
|
|
fn row_id(row: &Self::Row) -> Uuid;
|
|
|
|
fn from_row(row: Self::Row, crypto: &Crypto) -> Result<Self, LoadCredentialsError>;
|
|
|
|
// save_details needs to be implemented per-type because we don't know the number of parameters in advance
|
|
async fn save_details(&self, id: &Uuid, crypto: &Crypto, txn: &mut Transaction<'_, Sqlite>) -> Result<(), SaveCredentialsError>;
|
|
|
|
fn table_name() -> String {
|
|
format!("{}_credentials", Self::type_name())
|
|
}
|
|
|
|
async fn load(id: &Uuid, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
|
let q = format!("SELECT * FROM {} WHERE id = ?", Self::table_name());
|
|
let row: Self::Row = sqlx::query_as(&q)
|
|
.bind(id)
|
|
.fetch_optional(pool)
|
|
.await?
|
|
.ok_or(LoadCredentialsError::NoCredentials)?;
|
|
|
|
Self::from_row(row, crypto)
|
|
}
|
|
|
|
async fn load_by_name(name: &str, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
|
let q = format!(
|
|
"SELECT * FROM {} WHERE id = (SELECT id FROM credentials WHERE name = ?)",
|
|
Self::table_name(),
|
|
);
|
|
let row: Self::Row = sqlx::query_as(&q)
|
|
.bind(name)
|
|
.fetch_optional(pool)
|
|
.await?
|
|
.ok_or(LoadCredentialsError::NoCredentials)?;
|
|
|
|
Self::from_row(row, crypto)
|
|
}
|
|
|
|
async fn load_default(crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
|
|
let q = format!(
|
|
"SELECT details.*
|
|
FROM {} details
|
|
JOIN credentials c
|
|
ON c.id = details.id
|
|
AND c.is_default = 1",
|
|
Self::table_name(),
|
|
);
|
|
let row: Self::Row = sqlx::query_as(&q)
|
|
.fetch_optional(pool)
|
|
.await?
|
|
.ok_or(LoadCredentialsError::NoCredentials)?;
|
|
|
|
Self::from_row(row, crypto)
|
|
}
|
|
|
|
async fn list(crypto: &Crypto, pool: &SqlitePool) -> Result<Vec<(Uuid, Credential)>, LoadCredentialsError> {
|
|
let q = format!(
|
|
"SELECT details.*
|
|
FROM
|
|
{} details
|
|
JOIN credentials c
|
|
ON c.id = details.id
|
|
ORDER BY c.created_at",
|
|
Self::table_name(),
|
|
);
|
|
let mut rows = sqlx::query_as::<_, Self::Row>(&q).fetch(pool);
|
|
|
|
let mut creds = Vec::new();
|
|
while let Some(row) = rows.try_next().await? {
|
|
let id = Self::row_id(&row);
|
|
let cred = Self::from_row(row, crypto)?.into_credential();
|
|
creds.push((id, cred));
|
|
}
|
|
|
|
Ok(creds)
|
|
}
|
|
}
|