fix login mod

This commit is contained in:
Quentin 2023-11-21 15:09:39 +01:00
parent 6e8b2cfc9f
commit a7c9d554f6
Signed by: quentin
GPG key ID: E9602264D639FF68
5 changed files with 62 additions and 108 deletions

View file

@ -1,8 +1,8 @@
pub mod ldap_provider; pub mod ldap_provider;
pub mod static_provider; pub mod static_provider;
use std::collections::BTreeMap;
use std::sync::Arc; use std::sync::Arc;
use futures::try_join;
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use async_trait::async_trait; use async_trait::async_trait;
@ -122,13 +122,14 @@ impl CryptoKeys {
let password_blob = [&kdf_salt[..], &password_sealed].concat(); let password_blob = [&kdf_salt[..], &password_sealed].concat();
// Write values to storage // Write values to storage
k2v.insert_batch(&[ // @FIXME Implement insert batch in the storage API
k2v_insert_single_key("keys", "salt", salt_ct, ident_salt), let (salt, public, passwd) = (
k2v_insert_single_key("keys", "public", public_ct, keys.public), salt_ct.set_value(&ident_salt),
k2v_insert_single_key("keys", &password_sortkey, None, &password_blob), public_ct.set_value(keys.public.as_ref()),
]) k2v.row("keys", &password_sortkey).set_value(&password_blob)
.await );
.context("InsertBatch for salt, public, and password")?; try_join!(salt.push(), public.push(), passwd.push())
.context("InsertBatch for salt, public, and password")?;
Ok(keys) Ok(keys)
} }
@ -155,12 +156,13 @@ impl CryptoKeys {
}; };
// Write values to storage // Write values to storage
k2v.insert_batch(&[ // @FIXME implement insert batch in the storage API
k2v_insert_single_key("keys", "salt", salt_ct, ident_salt), let (salt, public) = (
k2v_insert_single_key("keys", "public", public_ct, keys.public), salt_ct.set_value(&ident_salt),
]) public_ct.set_value(keys.public.as_ref()),
.await );
.context("InsertBatch for salt and public")?;
try_join!(salt.push(), public.push()).context("InsertBatch for salt and public")?;
Ok(keys) Ok(keys)
} }
@ -170,7 +172,7 @@ impl CryptoKeys {
user_secrets: &UserSecrets, user_secrets: &UserSecrets,
password: &str, password: &str,
) -> Result<Self> { ) -> Result<Self> {
let k2v = storage.k2v_client()?; let k2v = storage.row_store()?;
let (ident_salt, expected_public) = Self::load_salt_and_public(&k2v).await?; let (ident_salt, expected_public) = Self::load_salt_and_public(&k2v).await?;
// Generate short password digest (= password identity) // Generate short password digest (= password identity)
@ -178,20 +180,21 @@ impl CryptoKeys {
// Lookup password blob // Lookup password blob
let password_sortkey = format!("password:{}", hex::encode(&ident)); let password_sortkey = format!("password:{}", hex::encode(&ident));
let password_ref = k2v.row("keys", &password_sortkey);
let password_blob = { let password_blob = {
let mut val = match k2v.read_item("keys", &password_sortkey).await { let val = match password_ref.fetch().await {
Err(k2v_client::Error::NotFound) => { Err(StorageError::NotFound) => {
bail!("invalid password") bail!("invalid password")
} }
x => x?, x => x?,
}; };
if val.value.len() != 1 { if val.content().len() != 1 {
bail!("multiple values for password in storage"); bail!("multiple values for password in storage");
} }
match val.value.pop().unwrap() { match val.content().pop().unwrap() {
K2vValue::Value(v) => v, Alternative::Value(v) => v,
K2vValue::Tombstone => bail!("invalid password"), Alternative::Tombstone => bail!("invalid password"),
} }
}; };
@ -258,26 +261,24 @@ impl CryptoKeys {
let password_blob = [&kdf_salt[..], &password_sealed].concat(); let password_blob = [&kdf_salt[..], &password_sealed].concat();
// List existing passwords to overwrite existing entry if necessary // List existing passwords to overwrite existing entry if necessary
let ct = match k2v.read_item("keys", &password_sortkey).await { let pass_key = k2v.row("keys", &password_sortkey);
Err(k2v_client::Error::NotFound) => None, let passwd = match pass_key.fetch().await {
Err(StorageError::NotFound) => pass_key,
v => { v => {
let entry = v?; let entry = v?;
if entry.value.iter().any(|x| matches!(x, K2vValue::Value(_))) { if entry.content().iter().any(|x| matches!(x, Alternative::Value(_))) {
bail!("password already exists"); bail!("password already exists");
} }
Some(entry.causality) entry.to_ref()
} }
}; };
// Write values to storage // Write values to storage
k2v.insert_batch(&[k2v_insert_single_key( passwd
"keys", .set_value(&password_blob)
&password_sortkey, .push()
ct, .await
&password_blob, .context("InsertBatch for new password")?;
)])
.await
.context("InsertBatch for new password")?;
Ok(()) Ok(())
} }
@ -287,7 +288,7 @@ impl CryptoKeys {
password: &str, password: &str,
allow_delete_all: bool, allow_delete_all: bool,
) -> Result<()> { ) -> Result<()> {
let k2v = storage.row_client()?; let k2v = storage.row_store()?;
let (ident_salt, _public) = Self::load_salt_and_public(&k2v).await?; let (ident_salt, _public) = Self::load_salt_and_public(&k2v).await?;
// Generate short password digest (= password identity) // Generate short password digest (= password identity)
@ -299,22 +300,23 @@ impl CryptoKeys {
// Check password is there // Check password is there
let pw = existing_passwords let pw = existing_passwords
.get(&password_sortkey) .iter()
.map(|x| x.to_ref())
.find(|x| x.key().1 == &password_sortkey)
//.get(&password_sortkey)
.ok_or(anyhow!("password does not exist"))?; .ok_or(anyhow!("password does not exist"))?;
if !allow_delete_all && existing_passwords.len() < 2 { if !allow_delete_all && existing_passwords.len() < 2 {
bail!("No other password exists, not deleting last password."); bail!("No other password exists, not deleting last password.");
} }
k2v.delete_item("keys", &password_sortkey, pw.causality.clone()) pw.rm().await.context("DeleteItem for password")?;
.await
.context("DeleteItem for password")?;
Ok(()) Ok(())
} }
// ---- STORAGE UTIL ---- // ---- STORAGE UTIL ----
//
async fn check_uninitialized( async fn check_uninitialized(
k2v: &RowStore, k2v: &RowStore,
) -> Result<(RowRef, RowRef)> { ) -> Result<(RowRef, RowRef)> {
@ -346,32 +348,29 @@ impl CryptoKeys {
Ok((salt_ct, public_ct)) Ok((salt_ct, public_ct))
} }
pub async fn load_salt_and_public(k2v: &K2vClient) -> Result<([u8; 32], PublicKey)> { pub async fn load_salt_and_public(k2v: &RowStore) -> Result<([u8; 32], PublicKey)> {
let mut params = k2v let params = k2v
.read_batch(&[ .select(Selector::List(vec![
k2v_read_single_key("keys", "salt", false), ("keys", "salt"),
k2v_read_single_key("keys", "public", false), ("keys", "public"),
]) ]))
.await .await
.context("ReadBatch for salt and public in load_salt_and_public")?; .context("ReadBatch for salt and public in load_salt_and_public")?;
if params.len() != 2 { if params.len() != 2 {
bail!( bail!(
"Invalid response from k2v storage: {:?} (expected two items)", "Invalid response from k2v storage: {:?} (expected two items)",
params params
); );
} }
if params[0].items.len() != 1 || params[1].items.len() != 1 { if params[0].content().len() != 1 || params[1].content().len() != 1 {
bail!("cryptographic keys not initialized for user"); bail!("cryptographic keys not initialized for user");
} }
// Retrieve salt from given response // Retrieve salt from given response
let salt_vals = &mut params[0].items.iter_mut().next().unwrap().1.value; let salt: Vec<u8> = match &mut params[0].content().iter_mut().next().unwrap() {
if salt_vals.len() != 1 { Alternative::Value(v) => std::mem::take(v),
bail!("Multiple values for `salt`"); Alternative::Tombstone => bail!("salt is a tombstone"),
}
let salt: Vec<u8> = match &mut salt_vals[0] {
K2vValue::Value(v) => std::mem::take(v),
K2vValue::Tombstone => bail!("salt is a tombstone"),
}; };
if salt.len() != 32 { if salt.len() != 32 {
bail!("`salt` is not 32 bytes long"); bail!("`salt` is not 32 bytes long");
@ -380,40 +379,21 @@ impl CryptoKeys {
salt_constlen.copy_from_slice(&salt); salt_constlen.copy_from_slice(&salt);
// Retrieve public from given response // Retrieve public from given response
let public_vals = &mut params[1].items.iter_mut().next().unwrap().1.value; let public: Vec<u8> = match &mut params[1].content().iter_mut().next().unwrap() {
if public_vals.len() != 1 { Alternative::Value(v) => std::mem::take(v),
bail!("Multiple values for `public`"); Alternative::Tombstone => bail!("public is a tombstone"),
}
let public: Vec<u8> = match &mut public_vals[0] {
K2vValue::Value(v) => std::mem::take(v),
K2vValue::Tombstone => bail!("public is a tombstone"),
}; };
let public = PublicKey::from_slice(&public).ok_or(anyhow!("Invalid public key length"))?; let public = PublicKey::from_slice(&public).ok_or(anyhow!("Invalid public key length"))?;
Ok((salt_constlen, public)) Ok((salt_constlen, public))
} }
async fn list_existing_passwords(k2v: &K2vClient) -> Result<BTreeMap<String, CausalValue>> { async fn list_existing_passwords(k2v: &RowStore) -> Result<Vec<RowValue>> {
let mut res = k2v let res = k2v.select(Selector::Prefix { shard_key: "keys", prefix: "password:" })
.read_batch(&[BatchReadOp {
partition_key: "keys",
filter: Filter {
start: None,
end: None,
prefix: Some("password:"),
limit: None,
reverse: false,
},
conflicts_only: false,
tombstones: false,
single_item: false,
}])
.await .await
.context("ReadBatch for prefix password: in list_existing_passwords")?; .context("ReadBatch for prefix password: in list_existing_passwords")?;
if res.len() != 1 {
bail!("unexpected k2v result: {:?}, expected one item", res); Ok(res)
}
Ok(res.pop().unwrap().items)
} }
fn serialize(&self) -> [u8; 64] { fn serialize(&self) -> [u8; 64] {

View file

@ -51,31 +51,12 @@ impl LoginProvider for StaticLoginProvider {
bail!("Wrong password"); bail!("Wrong password");
} }
/*
tracing::debug!(user=%username, "fetch bucket");
let bucket = user
.bucket
.clone()
.or_else(|| self.default_bucket.clone())
.ok_or(anyhow!(
"No bucket configured and no default bucket specified"
))?;*/
tracing::debug!(user=%username, "fetch keys"); tracing::debug!(user=%username, "fetch keys");
let storage: storage::Builders = match user.storage { let storage: storage::Builders = match user.storage {
StaticStorage::InMemory => Box::new(storage::in_memory::FullMem {}), StaticStorage::InMemory => Box::new(storage::in_memory::FullMem {}),
StaticStorage::Garage(c) => Box::new(storage::garage::GrgCreds {}), StaticStorage::Garage(c) => Box::new(storage::garage::GrgCreds {}),
}; };
/*
StorageCredentials {
k2v_region: self.k2v_region.clone(),
s3_region: self.s3_region.clone(),
aws_access_key_id: user.aws_access_key_id.clone(),
aws_secret_access_key: user.aws_secret_access_key.clone(),
bucket,
};*/
let keys = match (&user.master_key, &user.secret_key) { let keys = match (&user.master_key, &user.secret_key) {
(Some(m), Some(s)) => { (Some(m), Some(s)) => {
let master_key = let master_key =

View file

@ -53,7 +53,7 @@ impl IRowRef for GrgRef {
unimplemented!(); unimplemented!();
} }
fn set_value(&self, content: Vec<u8>) -> RowValue { fn set_value(&self, content: &[u8]) -> RowValue {
unimplemented!(); unimplemented!();
} }
fn fetch(&self) -> AsyncResult<RowValue> { fn fetch(&self) -> AsyncResult<RowValue> {

View file

@ -55,7 +55,7 @@ impl IRowRef for MemRef {
unimplemented!(); unimplemented!();
}*/ }*/
fn set_value(&self, content: Vec<u8>) -> RowValue { fn set_value(&self, content: &[u8]) -> RowValue {
unimplemented!(); unimplemented!();
} }
fn fetch(&self) -> AsyncResult<RowValue> { fn fetch(&self) -> AsyncResult<RowValue> {

View file

@ -95,21 +95,14 @@ pub type RowStore = Box<dyn IRowStore + Sync + Send>;
pub trait IRowRef: std::fmt::Debug pub trait IRowRef: std::fmt::Debug
{ {
/*fn clone_boxed(&self) -> RowRef;*/
fn to_orphan(&self) -> OrphanRowRef; fn to_orphan(&self) -> OrphanRowRef;
fn key(&self) -> (&str, &str); fn key(&self) -> (&str, &str);
fn set_value(&self, content: Vec<u8>) -> RowValue; fn set_value(&self, content: &[u8]) -> RowValue;
fn fetch(&self) -> AsyncResult<RowValue>; fn fetch(&self) -> AsyncResult<RowValue>;
fn rm(&self) -> AsyncResult<()>; fn rm(&self) -> AsyncResult<()>;
fn poll(&self) -> AsyncResult<RowValue>; fn poll(&self) -> AsyncResult<RowValue>;
} }
pub type RowRef = Box<dyn IRowRef + Send + Sync>; pub type RowRef = Box<dyn IRowRef + Send + Sync>;
/*impl Clone for RowRef {
fn clone(&self) -> Self {
return self.clone_boxed()
}
}*/
pub trait IRowValue: std::fmt::Debug pub trait IRowValue: std::fmt::Debug
{ {