Implement the set password method
This commit is contained in:
parent
f0517ef0b7
commit
1908d0a0d7
|
@ -9,7 +9,7 @@ repository = "https://github.com/panicbit/northstar"
|
||||||
documentation = "https://docs.rs/northstar"
|
documentation = "https://docs.rs/northstar"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
user_management = ["sled", "bincode", "serde/derive", "bcrypt", "crc32fast"]
|
user_management = ["sled", "bincode", "serde/derive", "rust-argon2", "crc32fast", "ring"]
|
||||||
default = ["serve_dir"]
|
default = ["serve_dir"]
|
||||||
serve_dir = ["mime_guess", "tokio/fs"]
|
serve_dir = ["mime_guess", "tokio/fs"]
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ bincode = { version = "1.3.1", optional = true }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
rust-argon2 = { version = "0.8.2", optional = true }
|
rust-argon2 = { version = "0.8.2", optional = true }
|
||||||
crc32fast = { version = "1.2.1", optional = true }
|
crc32fast = { version = "1.2.1", optional = true }
|
||||||
|
ring = { version = "0.16.15", optional = true }
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "user_management"
|
name = "user_management"
|
||||||
|
|
|
@ -26,6 +26,22 @@ use crate::user_management::UserManager;
|
||||||
use crate::user_management::Result;
|
use crate::user_management::Result;
|
||||||
use crate::user_management::manager::CertificateData;
|
use crate::user_management::manager::CertificateData;
|
||||||
|
|
||||||
|
const ARGON2_CONFIG: argon2::Config = argon2::Config {
|
||||||
|
ad: &[],
|
||||||
|
hash_length: 32,
|
||||||
|
lanes: 1,
|
||||||
|
mem_cost: 4096,
|
||||||
|
secret: &[],
|
||||||
|
thread_mode: argon2::ThreadMode::Sequential,
|
||||||
|
time_cost: 3,
|
||||||
|
variant: argon2::Variant::Argon2id,
|
||||||
|
version: argon2::Version::Version13,
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
static ref RANDOM: ring::rand::SystemRandom = ring::rand::SystemRandom::new();
|
||||||
|
}
|
||||||
|
|
||||||
/// An struct corresponding to the data stored in the user tree
|
/// An struct corresponding to the data stored in the user tree
|
||||||
///
|
///
|
||||||
/// In order to generate a full user obj, you need to perform a lookup with a specific
|
/// In order to generate a full user obj, you need to perform a lookup with a specific
|
||||||
|
@ -34,7 +50,7 @@ use crate::user_management::manager::CertificateData;
|
||||||
pub struct PartialUser<UserData> {
|
pub struct PartialUser<UserData> {
|
||||||
pub data: UserData,
|
pub data: UserData,
|
||||||
pub certificates: Vec<u32>,
|
pub certificates: Vec<u32>,
|
||||||
pub pass_hash: Option<String>,
|
pub pass_hash: Option<(Vec<u8>, [u8; 32])>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<UserData> PartialUser<UserData> {
|
impl<UserData> PartialUser<UserData> {
|
||||||
|
@ -46,11 +62,6 @@ impl<UserData> PartialUser<UserData> {
|
||||||
&self.certificates
|
&self.certificates
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The bcrypt hash of the user's password
|
|
||||||
pub fn pass_hash(&self) -> Option<&str> {
|
|
||||||
self.pass_hash.as_deref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write user data to the database
|
/// Write user data to the database
|
||||||
///
|
///
|
||||||
/// This MUST be called if the user data is modified using the AsMut trait, or else
|
/// This MUST be called if the user data is modified using the AsMut trait, or else
|
||||||
|
@ -258,13 +269,48 @@ impl<UserData: Serialize + DeserializeOwned> SignedInUser<UserData> {
|
||||||
&self,
|
&self,
|
||||||
try_password: impl AsRef<[u8]>
|
try_password: impl AsRef<[u8]>
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
if let Some(hash) = &self.inner.pass_hash {
|
if let Some((hash, salt)) = &self.inner.pass_hash {
|
||||||
Ok(argon2::verify_encoded(hash.as_str(), try_password.as_ref())?)
|
Ok(argon2::verify_raw(
|
||||||
|
try_password.as_ref(),
|
||||||
|
salt,
|
||||||
|
hash.as_ref(),
|
||||||
|
&ARGON2_CONFIG,
|
||||||
|
)?)
|
||||||
} else {
|
} else {
|
||||||
Err(super::UserManagerError::PasswordNotSet)
|
Err(super::UserManagerError::PasswordNotSet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Set's the password for this user
|
||||||
|
///
|
||||||
|
/// By default, users have no password, meaning the cannot add any certificates beyond
|
||||||
|
/// the one they created their account with. However, by setting their password,
|
||||||
|
/// users are able to add more devices to their account, and recover their account if
|
||||||
|
/// it's lost. Note that this will completely overwrite the users old password.
|
||||||
|
///
|
||||||
|
/// Use [`SignedInUser::check_password()`] and [`NotSignedInUser::attach()`] to check
|
||||||
|
/// the password against another one, or to link a new certificate.
|
||||||
|
///
|
||||||
|
/// Because this method uses a key derivation algorithm, this should be considered a
|
||||||
|
/// very expensive operation.
|
||||||
|
pub fn set_password(
|
||||||
|
&mut self,
|
||||||
|
password: impl AsRef<[u8]>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let salt: [u8; 32] = ring::rand::generate(&*RANDOM)
|
||||||
|
.expect("Error generating random salt")
|
||||||
|
.expose();
|
||||||
|
self.inner.pass_hash = Some((
|
||||||
|
argon2::hash_raw(
|
||||||
|
password.as_ref(),
|
||||||
|
salt.as_ref(),
|
||||||
|
&ARGON2_CONFIG,
|
||||||
|
)?,
|
||||||
|
salt,
|
||||||
|
));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Write any updates to the user to the database.
|
/// Write any updates to the user to the database.
|
||||||
///
|
///
|
||||||
/// Updates caused by calling methods directly on the user do not need to be saved.
|
/// Updates caused by calling methods directly on the user do not need to be saved.
|
||||||
|
|
Loading…
Reference in a new issue