diff --git a/ee/tabby-webserver/graphql/schema.graphql b/ee/tabby-webserver/graphql/schema.graphql index d73974a..8559f98 100644 --- a/ee/tabby-webserver/graphql/schema.graphql +++ b/ee/tabby-webserver/graphql/schema.graphql @@ -10,6 +10,7 @@ enum WorkerKind { type Mutation { resetRegistrationToken: String! + resetUserAuthToken: Boolean! register(email: String!, password1: String!, password2: String!, invitationCode: String): RegisterResponse! tokenAuth(email: String!, password: String!): TokenAuthResponse! verifyToken(token: String!): VerifyTokenResponse! diff --git a/ee/tabby-webserver/src/schema/auth.rs b/ee/tabby-webserver/src/schema/auth.rs index 648174f..4e12ab7 100644 --- a/ee/tabby-webserver/src/schema/auth.rs +++ b/ee/tabby-webserver/src/schema/auth.rs @@ -274,6 +274,8 @@ pub trait AuthenticationService: Send + Sync { async fn create_invitation(&self, email: String) -> Result; async fn list_invitations(&self) -> Result>; async fn delete_invitation(&self, id: i32) -> Result; + + async fn reset_user_auth_token(&self, email: &str) -> Result<()>; } #[cfg(test)] diff --git a/ee/tabby-webserver/src/schema/mod.rs b/ee/tabby-webserver/src/schema/mod.rs index e1ceec0..ccba4d7 100644 --- a/ee/tabby-webserver/src/schema/mod.rs +++ b/ee/tabby-webserver/src/schema/mod.rs @@ -144,6 +144,18 @@ impl Mutation { )) } + async fn reset_user_auth_token(ctx: &Context) -> Result { + if let Some(claims) = &ctx.claims { + ctx.locator + .auth() + .reset_user_auth_token(&claims.sub) + .await?; + Ok(true) + } else { + Err(CoreError::Unauthorized("You're not logged in")) + } + } + async fn register( ctx: &Context, email: String, diff --git a/ee/tabby-webserver/src/service/auth.rs b/ee/tabby-webserver/src/service/auth.rs index 9e1b84b..98f81bb 100644 --- a/ee/tabby-webserver/src/service/auth.rs +++ b/ee/tabby-webserver/src/service/auth.rs @@ -292,6 +292,10 @@ impl AuthenticationService for DbConn { async fn delete_invitation(&self, id: i32) -> Result { self.delete_invitation(id).await } + + async fn reset_user_auth_token(&self, email: &str) -> Result<()> { + self.reset_user_auth_token_by_email(email).await + } } fn password_hash(raw: &str) -> password_hash::Result { @@ -460,4 +464,16 @@ mod tests { // expire time should be no change assert_eq!(resp1.refresh_expires_at, resp2.refresh_expires_at); } + + #[tokio::test] + async fn test_reset_user_auth_token() { + let conn = DbConn::new_in_memory().await.unwrap(); + register_admin_user(&conn).await; + + let user = conn.get_user_by_email(ADMIN_EMAIL).await.unwrap().unwrap(); + conn.reset_user_auth_token(&user.email).await.unwrap(); + + let user2 = conn.get_user_by_email(ADMIN_EMAIL).await.unwrap().unwrap(); + assert_ne!(user.auth_token, user2.auth_token); + } } diff --git a/ee/tabby-webserver/src/service/db/users.rs b/ee/tabby-webserver/src/service/db/users.rs index 160395a..64e24ff 100644 --- a/ee/tabby-webserver/src/service/db/users.rs +++ b/ee/tabby-webserver/src/service/db/users.rs @@ -162,16 +162,17 @@ impl DbConn { id.is_ok() } - pub async fn reset_auth_token(&self, id: i32) -> Result { + pub async fn reset_user_auth_token_by_email(&self, email: &str) -> Result<()> { + let email = email.to_owned(); self.conn .call(move |c| { - let mut stmt = c.prepare(r#"UPDATE users SET auth_token = ? WHERE id = ?"#)?; - stmt.execute((Uuid::new_v4().to_string(), id))?; + let mut stmt = c.prepare(r#"UPDATE users SET auth_token = ? WHERE email = ?"#)?; + stmt.execute((generate_auth_token(), email))?; Ok(()) }) .await?; - Ok(id) + Ok(()) } } @@ -215,7 +216,9 @@ mod tests { assert!(conn.verify_auth_token(&user.auth_token).await); - conn.reset_auth_token(id).await.unwrap(); + conn.reset_user_auth_token_by_email(&user.email) + .await + .unwrap(); let new_user = conn.get_user(id).await.unwrap().unwrap(); assert_eq!(user.email, new_user.email); assert_ne!(user.auth_token, new_user.auth_token);