feat(api): added overwriting of zombie account on registration

This commit is contained in:
antifallobst 2023-08-18 00:09:29 +02:00
parent cb354ca7b2
commit 31a8c80eca
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
3 changed files with 68 additions and 21 deletions

View File

@ -105,4 +105,15 @@ impl Account {
Err(_) => Ok(false), Err(_) => Ok(false),
} }
} }
pub async fn delete(&self, pool: &PgPool) -> Result<()> {
sqlx::query!(r#"DELETE FROM AuthTokens WHERE account = $1;"#, self.id)
.execute(pool)
.await?;
sqlx::query!(r#"DELETE FROM Accounts WHERE id = $1;"#, self.id)
.execute(pool)
.await?;
Ok(())
}
} }

View File

@ -41,7 +41,11 @@ pub async fn register(
return Ok(data::RegisterResponse::MalformedEmail); return Ok(data::RegisterResponse::MalformedEmail);
} }
if Account::from_username(pool, &request.username) // Check if the usernam is already taken
if let Some(account) = Account::from_username(pool, &request.username).await? {
// Check if the account that has taken the username is verified or has an open verification request.
if account.verified
|| VerificationToken::from_id(pool, account.id)
.await? .await?
.is_some() .is_some()
{ {
@ -49,12 +53,23 @@ pub async fn register(
data::RegisterConflict::Username, data::RegisterConflict::Username,
)); ));
} }
// The found account is a Zombie account, we can delete it and continue.
account.delete(pool).await?;
}
if Account::from_email(pool, &request.email).await?.is_some() { // The same stuff for the email
if let Some(account) = Account::from_email(pool, &request.email).await? {
if account.verified
|| VerificationToken::from_id(pool, account.id)
.await?
.is_some()
{
return Ok(data::RegisterResponse::Conflict( return Ok(data::RegisterResponse::Conflict(
data::RegisterConflict::Email, data::RegisterConflict::Email,
)); ));
} }
account.delete(pool).await?;
}
let account = Account::new(pool, &request.username, &request.email, &request.password).await?; let account = Account::new(pool, &request.username, &request.email, &request.password).await?;
@ -137,16 +152,14 @@ pub async fn delete(pool: &PgPool, auth: String) -> Result<data::DeleteResponse>
None => return Ok(data::DeleteResponse::Unauthorized), None => return Ok(data::DeleteResponse::Unauthorized),
}; };
sqlx::query!( match Account::from_id(pool, token.account).await? {
r#"DELETE FROM AuthTokens WHERE account = $1;"#, Some(a) => a.delete(pool).await?,
token.account None => {
) return Err(anyhow::Error::msg(
.execute(pool) "Failed to delete account. Account not found in database",
.await?; ))
}
sqlx::query!(r#"DELETE FROM Accounts WHERE id = $1;"#, token.account) }
.execute(pool)
.await?;
Ok(data::DeleteResponse::Success) Ok(data::DeleteResponse::Success)
} }

View File

@ -156,6 +156,29 @@ impl VerificationToken {
} }
} }
pub async fn from_id(pool: &PgPool, account_id: i64) -> Result<Option<Self>> {
let query_result = sqlx::query_as!(
Self,
r#"SELECT * FROM VerificationTokens WHERE account = $1;"#,
account_id
)
.fetch_one(pool)
.await;
match query_result {
Ok(token) => {
if token.expire.timestamp() > chrono::Utc::now().timestamp() {
Ok(Some(token))
} else {
token.delete(pool).await?;
Ok(None)
}
}
Err(sqlx::Error::RowNotFound) => Ok(None),
Err(e) => Err(Error::new(e)),
}
}
pub async fn delete(&self, pool: &PgPool) -> Result<()> { pub async fn delete(&self, pool: &PgPool) -> Result<()> {
sqlx::query!( sqlx::query!(
r#"DELETE FROM VerificationTokens WHERE token = $1;"#, r#"DELETE FROM VerificationTokens WHERE token = $1;"#,