refactor(webserver): cleanup user crud (#935)

* refactor(webserver): cleanup user crud

* fix
add-signin-page
Meng Zhang 2023-12-02 00:56:15 +08:00 committed by GitHub
parent 19d773e7ad
commit fb879e5ddf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 87 additions and 17 deletions

View File

@ -128,7 +128,8 @@ impl AuthenticationService for DbConn {
ValidationErrors { errors }.into_field_error() ValidationErrors { errors }.into_field_error()
})?; })?;
if self.is_admin_initialized().await? { let is_admin_initialized = self.is_admin_initialized().await?;
if is_admin_initialized {
let err = Err("Invitation code is not valid".into()); let err = Err("Invitation code is not valid".into());
let Some(invitation_code) = invitation_code else { let Some(invitation_code) = invitation_code else {
return err; return err;
@ -150,9 +151,9 @@ impl AuthenticationService for DbConn {
let pwd_hash = password_hash(&input.password1)?; let pwd_hash = password_hash(&input.password1)?;
self.create_user(input.email.clone(), pwd_hash, false) let id = self.create_user(input.email.clone(), pwd_hash, !is_admin_initialized)
.await?; .await?;
let user = self.get_user_by_email(&input.email).await?.unwrap(); let user = self.get_user(id).await?.unwrap();
let access_token = generate_jwt(Claims::new(UserInfo::new( let access_token = generate_jwt(Claims::new(UserInfo::new(
user.email.clone(), user.email.clone(),

View File

@ -167,25 +167,35 @@ impl DbConn {
email: String, email: String,
password_encrypted: String, password_encrypted: String,
is_admin: bool, is_admin: bool,
) -> Result<()> { ) -> Result<i32> {
let res = self let res = self
.conn .conn
.call(move |c| { .call(move |c| {
c.execute( let mut stmt = c.prepare(
r#"INSERT INTO users (email, password_encrypted, is_admin) VALUES (?, ?, ?)"#, r#"INSERT INTO users (email, password_encrypted, is_admin) VALUES (?, ?, ?)"#,
params![email, password_encrypted, is_admin], )?;
) let id = stmt.insert((email, password_encrypted, is_admin))?;
Ok(id)
}) })
.await?; .await?;
if res != 1 {
return Err(anyhow::anyhow!("failed to create user"));
}
Ok(()) Ok(res as i32)
}
pub async fn get_user(&self, id: i32) -> Result<Option<User>> {
let user = self
.conn
.call(move |c| {
c.query_row(User::select("id = ?").as_str(), params![id], User::from_row)
.optional()
})
.await?;
Ok(user)
} }
pub async fn get_user_by_email(&self, email: &str) -> Result<Option<User>> { pub async fn get_user_by_email(&self, email: &str) -> Result<Option<User>> {
let email = email.to_string(); let email = email.to_owned();
let user = self let user = self
.conn .conn
.call(move |c| { .call(move |c| {
@ -292,6 +302,8 @@ impl DbConn {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use juniper::FieldResult;
use super::*; use super::*;
use crate::schema::auth::AuthenticationService; use crate::schema::auth::AuthenticationService;
@ -300,14 +312,13 @@ mod tests {
DbConn::init_db(conn).await DbConn::init_db(conn).await
} }
async fn create_admin_user(conn: &DbConn) -> String { async fn create_admin_user(conn: &DbConn) -> i32 {
let email = "test@example.com"; let email = "test@example.com";
let passwd = "123456"; let passwd = "123456";
let is_admin = true; let is_admin = true;
conn.create_user(email.to_string(), passwd.to_string(), is_admin) conn.create_user(email.to_string(), passwd.to_string(), is_admin)
.await .await
.unwrap(); .unwrap()
email.to_owned()
} }
#[tokio::test] #[tokio::test]
@ -337,8 +348,8 @@ mod tests {
async fn test_create_user() { async fn test_create_user() {
let conn = new_in_memory().await.unwrap(); let conn = new_in_memory().await.unwrap();
let email = create_admin_user(&conn).await; let id = create_admin_user(&conn).await;
let user = conn.get_user_by_email(&email).await.unwrap().unwrap(); let user = conn.get_user(id).await.unwrap().unwrap();
assert_eq!(user.id, 1); assert_eq!(user.id, 1);
} }
@ -385,4 +396,62 @@ mod tests {
let invitations = conn.list_invitations().await.unwrap(); let invitations = conn.list_invitations().await.unwrap();
assert!(invitations.is_empty()); assert!(invitations.is_empty());
} }
#[tokio::test]
async fn test_invitation_flow() {
let conn = new_in_memory().await.unwrap();
assert!(!conn.is_admin_initialized().await.unwrap());
create_admin_user(&conn).await;
let email = "user@user.com";
let password = "12345678";
conn.create_invitation(email.to_owned()).await.unwrap();
let invitation = &conn.list_invitations().await.unwrap()[0];
// Admin initialized, registeration requires a invitation code;
assert!(
conn.register(
email.to_owned(),
password.to_owned(),
password.to_owned(),
None
)
.await.is_err()
);
// Invalid invitation code won't work.
assert!(conn
.register(
email.to_owned(),
password.to_owned(),
password.to_owned(),
Some("abc".to_owned())
)
.await
.is_err());
// Register success.
assert!(conn
.register(
email.to_owned(),
password.to_owned(),
password.to_owned(),
Some(invitation.code.clone())
)
.await
.is_ok());
// Try register again with same email failed.
assert!(conn
.register(
email.to_owned(),
password.to_owned(),
password.to_owned(),
Some(invitation.code.clone())
)
.await
.is_err());
}
} }