aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-common/src
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@elliehuxtable.com>2024-06-24 14:54:54 +0100
committerGitHub <noreply@github.com>2024-06-24 14:54:54 +0100
commit67d64ec4b368c48188c746f2dba2967ec4615fe5 (patch)
tree9da8443d4baa424e99a806cb022d53daa6a8c30e /crates/atuin-common/src
parentfix: Some --help comments didn't show properly (#2176) (diff)
downloadatuin-67d64ec4b368c48188c746f2dba2967ec4615fe5.zip
feat: add user account verification (#2190)
* add verified column to users table * add database functions to check if verified, or to verify * getting there * verification check * use base64 urlsafe no pad * add verification client * clippy * correct docs * fix integration tests
Diffstat (limited to 'crates/atuin-common/src')
-rw-r--r--crates/atuin-common/src/api.rs16
-rw-r--r--crates/atuin-common/src/utils.rs33
2 files changed, 46 insertions, 3 deletions
diff --git a/crates/atuin-common/src/api.rs b/crates/atuin-common/src/api.rs
index 99b57cec..4e897811 100644
--- a/crates/atuin-common/src/api.rs
+++ b/crates/atuin-common/src/api.rs
@@ -34,6 +34,22 @@ pub struct RegisterResponse {
pub struct DeleteUserResponse {}
#[derive(Debug, Serialize, Deserialize)]
+pub struct SendVerificationResponse {
+ pub email_sent: bool,
+ pub verified: bool,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct VerificationTokenRequest {
+ pub token: String,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct VerificationTokenResponse {
+ pub verified: bool,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
pub struct ChangePasswordRequest {
pub current_password: String,
pub new_password: String,
diff --git a/crates/atuin-common/src/utils.rs b/crates/atuin-common/src/utils.rs
index 65f5efc4..869866b0 100644
--- a/crates/atuin-common/src/utils.rs
+++ b/crates/atuin-common/src/utils.rs
@@ -4,17 +4,30 @@ use std::path::PathBuf;
use eyre::{eyre, Result};
-use rand::RngCore;
+use base64::prelude::{Engine, BASE64_URL_SAFE_NO_PAD};
+use getrandom::getrandom;
use uuid::Uuid;
-pub fn random_bytes<const N: usize>() -> [u8; N] {
+/// Generate N random bytes, using a cryptographically secure source
+pub fn crypto_random_bytes<const N: usize>() -> [u8; N] {
+ // rand say they are in principle safe for crypto purposes, but that it is perhaps a better
+ // idea to use getrandom for things such as passwords.
let mut ret = [0u8; N];
- rand::thread_rng().fill_bytes(&mut ret);
+ getrandom(&mut ret).expect("Failed to generate random bytes!");
ret
}
+/// Generate N random bytes using a cryptographically secure source, return encoded as a string
+pub fn crypto_random_string<const N: usize>() -> String {
+ let bytes = crypto_random_bytes::<N>();
+
+ // We only use this to create a random string, and won't be reversing it to find the original
+ // data - no padding is OK there. It may be in URLs.
+ BASE64_URL_SAFE_NO_PAD.encode(bytes)
+}
+
pub fn uuid_v7() -> Uuid {
Uuid::now_v7()
}
@@ -178,6 +191,7 @@ impl<T: AsRef<str>> Escapable for T {}
#[cfg(test)]
mod tests {
+ use pretty_assertions::assert_ne;
use time::Month;
use super::*;
@@ -292,4 +306,17 @@ mod tests {
Cow::Owned(_)
));
}
+
+ #[test]
+ fn dumb_random_test() {
+ // Obviously not a test of randomness, but make sure we haven't made some
+ // catastrophic error
+
+ assert_ne!(crypto_random_string::<1>(), crypto_random_string::<1>());
+ assert_ne!(crypto_random_string::<2>(), crypto_random_string::<2>());
+ assert_ne!(crypto_random_string::<4>(), crypto_random_string::<4>());
+ assert_ne!(crypto_random_string::<8>(), crypto_random_string::<8>());
+ assert_ne!(crypto_random_string::<16>(), crypto_random_string::<16>());
+ assert_ne!(crypto_random_string::<32>(), crypto_random_string::<32>());
+ }
}