start refactoring for default credentials

This commit is contained in:
2024-06-26 11:10:50 -04:00
parent 8c668e51a6
commit 37b44ddb2e
21 changed files with 708 additions and 632 deletions

View File

@ -42,12 +42,13 @@ CREATE TABLE credentials (
id BLOB UNIQUE NOT NULL,
name TEXT UNIQUE NOT NULL,
type TEXT NOT NULL,
is_default BOOLEAN NOT NULL,
created_at INTEGER NOT NULL
);
-- populate with basic data from existing AWS credential
INSERT INTO credentials (id, name, type, created_at)
SELECT id, 'default', 'aws', created_at FROM aws_tmp;
INSERT INTO credentials (id, name, type, is_default, created_at)
SELECT id, 'default', 'aws', 1, created_at FROM aws_tmp;
-- new AWS-specific table
CREATE TABLE aws_credentials (

View File

@ -12,19 +12,27 @@ use serde::{
};
use serde::de::{self, Visitor};
use sqlx::{
SqlitePool,
FromRow,
Sqlite,
Transaction,
types::Uuid,
};
use sqlx::error::{
Error as SqlxError,
};
use tokio_stream::StreamExt;
use super::{Credential, Crypto, SaveCredential, PersistentCredential};
use super::{Crypto, PersistentCredential};
use crate::errors::*;
#[derive(Debug, Clone, FromRow)]
pub struct AwsRow {
#[allow(dead_code)]
id: Uuid,
access_key_id: String,
secret_key_enc: Vec<u8>,
nonce: Vec<u8>,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct AwsBaseCredential {
@ -42,85 +50,123 @@ impl AwsBaseCredential {
}
impl PersistentCredential for AwsBaseCredential {
async fn save(&self, id: &Uuid, name: &str, crypto: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError> {
type Row = AwsRow;
fn type_name() -> &'static str { "aws" }
fn from_row(row: AwsRow, crypto: &Crypto) -> Result<Self, LoadCredentialsError> {
let nonce = XNonce::clone_from_slice(&row.nonce);
let secret_key_bytes = crypto.decrypt(&nonce, &row.secret_key_enc)?;
let secret_key = String::from_utf8(secret_key_bytes)
.map_err(|_| LoadCredentialsError::InvalidData)?;
Ok(Self::new(row.access_key_id, secret_key))
}
async fn save_details(&self, id: &Uuid, crypto: &Crypto, txn: &mut Transaction<'_, Sqlite>) -> Result<(), SaveCredentialsError> {
let (nonce, ciphertext) = crypto.encrypt(self.secret_access_key.as_bytes())?;
let nonce_bytes = &nonce.as_slice();
let res = sqlx::query!(
"INSERT INTO credentials (id, name, type, created_at)
VALUES (?, ?, 'aws', strftime('%s'))
ON CONFLICT(id) DO UPDATE SET
name = excluded.name,
type = excluded.type,
created_at = excluded.created_at;
INSERT OR REPLACE INTO aws_credentials (
sqlx::query!(
"INSERT OR REPLACE INTO aws_credentials (
id,
access_key_id,
secret_key_enc,
nonce
)
VALUES (?, ?, ?, ?);",
id,
name,
id, // for the second query
self.access_key_id,
ciphertext,
nonce_bytes,
).execute(pool).await;
id, self.access_key_id, ciphertext, nonce_bytes,
).execute(&mut **txn).await?;
match res {
Err(SqlxError::Database(e)) if e.code().as_deref() == Some("2067") => Err(SaveCredentialsError::Duplicate),
Err(e) => Err(SaveCredentialsError::DbError(e)),
Ok(_) => Ok(())
}
Ok(())
}
async fn load(name: &str, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
let row = sqlx::query!(
"SELECT c.name, a.access_key_id, a.secret_key_enc, a.nonce
FROM credentials c JOIN aws_credentials a ON a.id = c.id
WHERE c.name = ?",
name
).fetch_optional(pool)
.await?
.ok_or(LoadCredentialsError::NoCredentials)?;
// async fn save(&self, record: CredentialRecord, &Crypto, pool: &SqlitePool) -> Result<(), CredentialRecordsError> {
// let (nonce, ciphertext) = crypto.encrypt(self.secret_access_key.as_bytes())?;
// let nonce_bytes = &nonce.as_slice();
let nonce = XNonce::clone_from_slice(&row.nonce);
let secret_key_bytes = crypto.decrypt(&nonce, &row.secret_key_enc)?;
let secret_key = String::from_utf8(secret_key_bytes)
.map_err(|_| LoadCredentialsError::InvalidData)?;
// let res = sqlx::query!(
// "INSERT INTO credentials (id, name, type, created_at)
// VALUES (?, ?, 'aws', strftime('%s'))
// ON CONFLICT(id) DO UPDATE SET
// name = excluded.name,
// type = excluded.type,
// created_at = excluded.created_at;
// INSERT OR REPLACE INTO aws_credentials (
// id,
// access_key_id,
// secret_key_enc,
// nonce
// )
// VALUES (?, ?, ?, ?);",
// id,
// name,
// id, // for the second query
// self.access_key_id,
// ciphertext,
// nonce_bytes,
// ).execute(pool).await;
Ok(AwsBaseCredential::new(row.access_key_id, secret_key))
}
// match res {
// Err(SqlxError::Database(e)) if e.code().as_deref() == Some("2067") => Err(CredentialRecordsError::Duplicate),
// Err(e) => Err(SaveCredentialsError::DbError(e)),
// Ok(_) => Ok(())
// }
// }
async fn list(crypto: &Crypto, pool: &SqlitePool) -> Result<Vec<SaveCredential>, LoadCredentialsError> {
let mut rows = sqlx::query!(
"SELECT c.id, c.name, a.access_key_id, a.secret_key_enc, a.nonce
FROM credentials c JOIN aws_credentials a ON a.id = c.id"
).fetch(pool);
// async fn load(name: &str, crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
// let record: AwsRecord = sqlx::query_as(
// "SELECT c.id, c.name, c.is_default, a.access_key_id, a.secret_key_enc, a.nonce
// FROM credentials c JOIN aws_credentials a ON a.id = c.id
// WHERE c.name = ?"
// ).bind(name)
// .fetch_optional(pool)
// .await?
// .ok_or(LoadCredentialsError::NoCredentials)?;
let mut creds = Vec::new();
// let key = record.decrypt_key(crypto)?;
// let credential = AwsBaseCredential::new(record.access_key_id, key);
// Ok(credential)
// }
while let Some(row) = rows.try_next().await? {
let nonce = XNonce::clone_from_slice(&row.nonce);
let secret_key_bytes = crypto.decrypt(&nonce, &row.secret_key_enc)?;
let secret_key = String::from_utf8(secret_key_bytes)
.map_err(|_| LoadCredentialsError::InvalidData)?;
let aws = AwsBaseCredential::new(row.access_key_id, secret_key);
// async fn load_default(crypto: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError> {
// let record: AwsRecord = sqlx::query_as(
// "SELECT c.id, c.name, c.is_default, a.access_key_id, a.secret_key_enc, a.nonce
// FROM credentials c JOIN aws_credentials a ON a.id = c.id
// WHERE c.type = 'aws' AND c.is_default = 1"
// ).fetch_optional(pool)
// .await?
// .ok_or(LoadCredentialsError::NoCredentials)?;
let id = Uuid::from_slice(&row.id)
.map_err(|_| LoadCredentialsError::InvalidData)?;
// let key = record.decrypt_key(crypto)?;
// let credential = AwsBaseCredential::new(record.access_key_id, key);
// Ok(credential)
// }
let cred = SaveCredential {
id,
name: row.name,
credential: Credential::AwsBase(aws),
};
creds.push(cred);
}
// async fn list(crypto: &Crypto, pool: &SqlitePool) -> Result<Vec<SaveCredential>, LoadCredentialsError> {
// let mut rows = sqlx::query_as::<_, AwsRecord>(
// "SELECT c.id, c.name, c.is_default, a.access_key_id, a.secret_key_enc, a.nonce
// FROM credentials c JOIN aws_credentials a ON a.id = c.id"
// ).fetch(pool);
Ok(creds)
}
// let mut creds = Vec::new();
// while let Some(record) = rows.try_next().await? {
// let key = record.decrypt_key(crypto)?;
// let aws = AwsBaseCredential::new(record.access_key_id, key);
// let cred = SaveCredential {
// id: record.id,
// name: record.name,
// is_default: record.is_default,
// credential: Credential::AwsBase(aws),
// };
// creds.push(cred);
// }
// Ok(creds)
// }
}
@ -223,6 +269,7 @@ where S: Serializer
#[cfg(test)]
mod tests {
use super::*;
use sqlx::SqlitePool;
fn test_creds() -> AwsBaseCredential {
@ -256,7 +303,8 @@ mod tests {
#[sqlx::test]
async fn test_save(pool: SqlitePool) {
let crypt = Crypto::random();
test_creds().save(&test_uuid_random(), "test", &crypt, &pool).await
let mut txn = pool.begin().await.unwrap();
test_creds().save_details(&test_uuid_random(), &crypt, &mut txn).await
.expect("Failed to save AWS credentials");
}
@ -267,11 +315,12 @@ mod tests {
let creds = test_creds_2();
// overwite original creds with different test data
creds.save(&test_uuid(), "test", &crypt, &pool).await
let mut txn = pool.begin().await.unwrap();
creds.save_details(&test_uuid(), &crypt, &mut txn).await
.expect("Failed to update AWS credentials");
// make sure update went through
let loaded = AwsBaseCredential::load("test", &crypt, &pool).await.unwrap();
let loaded = AwsBaseCredential::load(&test_uuid(), &crypt, &pool).await.unwrap();
assert_eq!(creds, loaded);
}
@ -281,7 +330,8 @@ mod tests {
let crypt = Crypto::random();
let id = test_uuid_random();
let resp = test_creds().save(&id, "test", &crypt, &pool).await;
let mut txn = pool.begin().await.unwrap();
let resp = test_creds().save_details(&id, &crypt, &mut txn).await;
if !matches!(resp, Err(SaveCredentialsError::Duplicate)) {
panic!("Attempt to create duplicate entry returned {resp:?}")
@ -292,7 +342,15 @@ mod tests {
#[sqlx::test(fixtures("aws_credentials"))]
async fn test_load(pool: SqlitePool) {
let crypt = Crypto::fixed();
let loaded = AwsBaseCredential::load("test", &crypt, &pool).await.unwrap();
let loaded = AwsBaseCredential::load(&test_uuid(), &crypt, &pool).await.unwrap();
assert_eq!(test_creds(), loaded);
}
#[sqlx::test(fixtures("aws_credentials"))]
async fn test_load_by_name(pool: SqlitePool) {
let crypt = Crypto::fixed();
let loaded = AwsBaseCredential::load_by_name("test", &crypt, &pool).await.unwrap();
assert_eq!(test_creds(), loaded);
}
@ -301,45 +359,48 @@ mod tests {
async fn test_save_load(pool: SqlitePool) {
let crypt = Crypto::random();
let creds = test_creds();
creds.save(&test_uuid_random(), "test", &crypt, &pool).await.unwrap();
let loaded = AwsBaseCredential::load("test", &crypt, &pool).await.unwrap();
let id = test_uuid_random();
let mut txn = pool.begin().await.unwrap();
creds.save_details(&id, &crypt, &mut txn).await.unwrap();
let loaded = AwsBaseCredential::load(&id, &crypt, &pool).await.unwrap();
assert_eq!(creds, loaded);
}
#[sqlx::test(fixtures("aws_credentials"))]
async fn test_list(pool: SqlitePool) {
let crypt = Crypto::fixed();
let list = AwsBaseCredential::list(&crypt, &pool).await
.expect("Failed to list AWS credentials");
// #[sqlx::test(fixtures("aws_credentials"))]
// async fn test_list(pool: SqlitePool) {
// let crypt = Crypto::fixed();
// let list = AwsBaseCredential::list(&crypt, &pool).await
// .expect("Failed to list AWS credentials");
let first = SaveCredential {
id: test_uuid(),
name: "test".into(),
credential: Credential::AwsBase(test_creds()),
};
assert_eq!(&first, &list[0]);
// let first = SaveCredential {
// id: test_uuid(),
// name: "test".into(),
// credential: Credential::AwsBase(test_creds()),
// };
// assert_eq!(&first, &list[0]);
let second = SaveCredential {
id: test_uuid_2(),
name: "test2".into(),
credential: Credential::AwsBase(test_creds_2()),
};
assert_eq!(&second, &list[1]);
}
// let second = SaveCredential {
// id: test_uuid_2(),
// name: "test2".into(),
// credential: Credential::AwsBase(test_creds_2()),
// };
// assert_eq!(&second, &list[1]);
// }
#[sqlx::test(fixtures("aws_credentials"))]
async fn test_rekey(pool: SqlitePool) {
let old_crypt = Crypto::fixed();
let orig = AwsBaseCredential::list(&old_crypt, &pool).await.unwrap();
// #[sqlx::test(fixtures("aws_credentials"))]
// async fn test_rekey(pool: SqlitePool) {
// let old_crypt = Crypto::fixed();
// let orig = AwsBaseCredential::list(&old_crypt, &pool).await.unwrap();
let new_crypt = Crypto::random();
AwsBaseCredential::rekey(&old_crypt, &new_crypt, &pool).await
.expect("Failed to re-key AWS credentials");
// let new_crypt = Crypto::random();
// AwsBaseCredential::rekey(&old_crypt, &new_crypt, &pool).await
// .expect("Failed to re-key AWS credentials");
let rekeyed = AwsBaseCredential::list(&new_crypt, &pool).await.unwrap();
for (before, after) in orig.iter().zip(rekeyed.iter()) {
assert_eq!(before, after);
}
}
// let rekeyed = AwsBaseCredential::list(&new_crypt, &pool).await.unwrap();
// for (before, after) in orig.iter().zip(rekeyed.iter()) {
// assert_eq!(before, after);
// }
// }
}

View File

@ -1,7 +1,7 @@
INSERT INTO credentials (id, name, type, created_at)
INSERT INTO credentials (id, name, type, is_default, created_at)
VALUES
(X'00000000000000000000000000000000', 'test', 'aws', strftime('%s')),
(X'ffffffffffffffffffffffffffffffff', 'test2', 'aws', strftime('%s'));
(X'00000000000000000000000000000000', 'test', 'aws', strftime('%s'), 1),
(X'ffffffffffffffffffffffffffffffff', 'test2', 'aws', strftime('%s'), 0);
INSERT INTO aws_credentials (id, access_key_id, secret_key_enc, nonce)
VALUES

View File

@ -24,8 +24,15 @@ use serde::{
Deserializer,
};
use serde::de::{self, Visitor};
use sqlx::SqlitePool;
use sqlx::types::Uuid;
use sqlx::{
Error as SqlxError,
FromRow,
Sqlite,
SqlitePool,
sqlite::SqliteRow,
Transaction,
types::Uuid,
};
use crate::errors::*;
use crate::kv;
@ -35,6 +42,7 @@ pub use aws::{AwsBaseCredential, AwsSessionCredential};
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum Credential {
AwsBase(AwsBaseCredential),
AwsSession(AwsSessionCredential),
@ -43,27 +51,74 @@ pub enum Credential {
// we need a special type for listing structs because
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct SaveCredential {
pub struct CredentialRecord {
#[serde(serialize_with = "serialize_uuid")]
#[serde(deserialize_with = "deserialize_uuid")]
id: Uuid, // UUID so it can be generated on the frontend
name: String, // user-facing identifier so it can be changed
is_default: bool,
credential: Credential,
}
impl SaveCredential {
pub async fn save(&self, crypt: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError> {
let cred = match &self.credential {
Credential::AwsBase(b) => b,
Credential::AwsSession(_) => return Err(SaveCredentialsError::NotPersistent),
impl CredentialRecord {
pub async fn save(&self, crypto: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError> {
let type_name = match &self.credential {
Credential::AwsBase(_) => AwsBaseCredential::type_name(),
_ => return Err(SaveCredentialsError::NotPersistent),
};
cred.save(&self.id, &self.name, crypt, pool).await
// if the credential being saved is default, make sure it's the only default of its type
let mut txn = pool.begin().await?;
if self.is_default {
sqlx::query!(
"UPDATE credentials SET is_default = 0 WHERE type = ?",
type_name
).execute(&mut *txn).await?;
}
// save to parent credentials table
let res = sqlx::query!(
"INSERT INTO credentials (id, name, type, is_default)
VALUES (?, ?, ?, ?)
ON CONFLICT DO UPDATE SET
name = excluded.name,
type = excluded.type,
is_default = excluded.is_default",
self.id, self.name, type_name, self.is_default
).execute(&mut *txn).await;
// if id is unique, but name is not, we will get an error
// (if id is not unique, this becomes an upsert due to ON CONFLICT clause)
match res {
Err(SqlxError::Database(e)) if e.is_unique_violation() => Err(SaveCredentialsError::Duplicate),
Err(e) => Err(SaveCredentialsError::DbError(e)),
Ok(_) => Ok(())
}?;
// save credential details to child table
match &self.credential {
Credential::AwsBase(b) => b.save_details(&self.id, crypto, &mut txn).await,
_ => Err(SaveCredentialsError::NotPersistent),
}?;
// make it real
txn.commit().await?;
Ok(())
}
#[allow(unused_variables)]
pub async fn list(crypto: &Crypto, pool: &SqlitePool) -> Result<Vec<Self>, LoadCredentialsError> {
todo!()
}
#[allow(unused_variables)]
pub async fn rekey(old: &Crypto, new: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError> {
todo!()
}
}
fn serialize_uuid<S: Serializer>(u: &Uuid, s: S) -> Result<S::Ok, S::Error> {
let mut buf = Vec::new();
let mut buf = Uuid::encode_buffer();
s.serialize_str(u.as_hyphenated().encode_lower(&mut buf))
}
@ -88,15 +143,57 @@ fn deserialize_uuid<'de, D: Deserializer<'de>>(ds: D) -> Result<Uuid, D::Error>
pub trait PersistentCredential: for<'a> Deserialize<'a> + Sized {
async fn load(name: &str, crypt: &Crypto, pool: &SqlitePool) -> Result<Self, LoadCredentialsError>;
async fn list(crypt: &Crypto, pool: &SqlitePool) -> Result<Vec<SaveCredential>, LoadCredentialsError>;
async fn save(&self, id: &Uuid, name: &str, crypt: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError>;
type Row: Send + Unpin + for<'r> FromRow<'r, SqliteRow>;
async fn rekey(old: &Crypto, new: &Crypto, pool: &SqlitePool) -> Result<(), SaveCredentialsError> {
for cred in Self::list(old, pool).await? {
cred.save(new, pool).await?;
}
Ok(())
fn type_name() -> &'static str;
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)
}
}
@ -300,14 +397,48 @@ impl Debug for Crypto {
}
// #[cfg(test)]
// mod tests {
// use super::*;
#[cfg(test)]
mod tests {
use super::*;
// #[sqlx::test(fixtures("uuid_test"))]
// async fn save_uuid(pool: SqlitePool) {
// let u = Uuid::try_parse("7140b90c-bfbd-4394-9008-01b94f94ecf8").unwrap();
// sqlx::query!("INSERT INTO uuids (uuid) VALUES (?)", u).execute(pool).unwrap();
// panic!("done, go check db");
// }
// }
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
struct UuidWrapper {
#[serde(serialize_with = "serialize_uuid")]
#[serde(deserialize_with = "deserialize_uuid")]
id: Uuid,
}
#[test]
fn test_serialize_uuid() {
let u = UuidWrapper {
id: Uuid::try_parse("693f84d2-4c1b-41e5-8483-cbe178324e04").unwrap()
};
let computed = serde_json::to_string(&u).unwrap();
assert_eq!(
"{\"id\":\"693f84d2-4c1b-41e5-8483-cbe178324e04\"}",
&computed,
);
}
#[test]
fn test_deserialize_uuid() {
let s = "{\"id\":\"045bd359-8630-4b76-9b7d-e4a86ed2222c\"}";
let computed = serde_json::from_str(s).unwrap();
let expected = UuidWrapper {
id: Uuid::try_parse("045bd359-8630-4b76-9b7d-e4a86ed2222c").unwrap(),
};
assert_eq!(expected, computed);
}
#[test]
fn test_serialize_deserialize_uuid() {
let buf = Crypto::salt();
let expected = UuidWrapper{
id: Uuid::from_slice(&buf[..16]).unwrap()
};
let serialized = serde_json::to_string(&expected).unwrap();
let computed = serde_json::from_str(&serialized).unwrap();
assert_eq!(expected, computed)
}
}

View File

@ -5,7 +5,7 @@ use tauri::State;
use crate::config::AppConfig;
use crate::credentials::{
AppSession,
SaveCredential
CredentialRecord
};
use crate::errors::*;
use crate::clientinfo::Client;
@ -107,10 +107,10 @@ pub async fn signal_activity(app_state: State<'_, AppState>) -> Result<(), ()> {
#[tauri::command]
pub async fn save_credential(
cred: SaveCredential,
record: CredentialRecord,
app_state: State<'_, AppState>
) -> Result<(), SaveCredentialsError> {
app_state.save_credential(cred).await
app_state.save_credential(record).await
}
@ -123,7 +123,7 @@ pub async fn delete_credential(id: &str, app_state: State<'_, AppState>) -> Resu
#[tauri::command]
pub async fn list_credentials(app_state: State<'_, AppState>) -> Result<Vec<SaveCredential>, GetCredentialsError> {
pub async fn list_credentials(app_state: State<'_, AppState>) -> Result<Vec<CredentialRecord>, GetCredentialsError> {
app_state.list_credentials().await
}

View File

@ -21,7 +21,7 @@ use crate::credentials::{
use crate::{config, config::AppConfig};
use crate::credentials::{
AwsBaseCredential,
SaveCredential,
CredentialRecord,
PersistentCredential
};
use crate::ipc::{self, RequestResponse};
@ -142,10 +142,10 @@ impl AppState {
}
}
pub async fn save_credential(&self, cred: SaveCredential) -> Result<(), SaveCredentialsError> {
pub async fn save_credential(&self, record: CredentialRecord) -> Result<(), SaveCredentialsError> {
let session = self.app_session.read().await;
let crypto = session.try_get_crypto()?;
cred.save(crypto, &self.pool).await
record.save(crypto, &self.pool).await
}
pub async fn delete_credential(&self, id: &Uuid) -> Result<(), SaveCredentialsError> {
@ -155,12 +155,11 @@ impl AppState {
Ok(())
}
pub async fn list_credentials(&self) -> Result<Vec<SaveCredential>, GetCredentialsError> {
pub async fn list_credentials(&self) -> Result<Vec<CredentialRecord>, GetCredentialsError> {
let session = self.app_session.read().await;
let crypto = session.try_get_crypto()?;
let creds = AwsBaseCredential::list(crypto, &self.pool).await?;
// eventual extend this vec with other credential types
Ok(creds)
let list = CredentialRecord::list(crypto, &self.pool).await?;
Ok(list)
}
pub async fn set_passphrase(&self, passphrase: &str) -> Result<(), SaveCredentialsError> {
@ -171,7 +170,7 @@ impl AppState {
let new_session = AppSession::new(passphrase)?;
if let AppSession::Unlocked {salt: _, ref crypto} = *cur_session {
AwsBaseCredential::rekey(
CredentialRecord::rekey(
crypto,
new_session.try_get_crypto().expect("AppSession::new() should always return Unlocked"),
&self.pool,
@ -258,7 +257,7 @@ impl AppState {
pub async fn get_aws_base(&self, name: &str) -> Result<AwsBaseCredential, GetCredentialsError> {
let app_session = self.app_session.read().await;
let crypto = app_session.try_get_crypto()?;
let creds = AwsBaseCredential::load(name, crypto, &self.pool).await?;
let creds = AwsBaseCredential::load_by_name(name, crypto, &self.pool).await?;
Ok(creds)
}
@ -345,12 +344,7 @@ mod tests {
state.delete_credential(&id).await.unwrap();
// ensure delete-cascade went through correctly
let res = AwsBaseCredential::load(
"test",
&Crypto::fixed(),
&state.pool,
).await;
let res = AwsBaseCredential::load(&id, &Crypto::fixed(), &state.pool).await;
assert!(matches!(res, Err(LoadCredentialsError::NoCredentials)));
}
}