Compare commits

..

2 Commits

4 changed files with 133 additions and 32 deletions

View File

@ -14,6 +14,9 @@ pub struct Account {
pub password: String, pub password: String,
pub joined: sqlx_chrono::NaiveDateTime, pub joined: sqlx_chrono::NaiveDateTime,
pub verified: bool, pub verified: bool,
pub follows: Option<Vec<i64>>,
pub followers: Option<Vec<i64>>,
pub permissions: i64,
} }
impl Account { impl Account {
@ -33,7 +36,7 @@ impl Account {
let joined = sqlx_chrono::Utc::now().naive_utc(); let joined = sqlx_chrono::Utc::now().naive_utc();
sqlx::query!( sqlx::query!(
r#"INSERT INTO Accounts (username, email, salt, password, joined, verified) VALUES ($1, $2, $3, $4, $5, false);"#, r#"INSERT INTO Accounts (username, email, salt, password, joined, verified, permissions) VALUES ($1, $2, $3, $4, $5, false, 0);"#,
username, username,
email, email,
salt.to_string(), salt.to_string(),
@ -54,7 +57,18 @@ impl Account {
pub async fn from_username(pool: &PgPool, username: &String) -> Result<Option<Self>> { pub async fn from_username(pool: &PgPool, username: &String) -> Result<Option<Self>> {
match sqlx::query_as!( match sqlx::query_as!(
Self, Self,
r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE username = $1;"#, r#"SELECT
id,
username,
email,
salt,
password,
joined,
verified as "verified!: bool",
follows,
followers,
permissions
FROM Accounts WHERE username = $1;"#,
username username
) )
.fetch_one(pool) .fetch_one(pool)
@ -69,7 +83,18 @@ impl Account {
pub async fn from_id(pool: &PgPool, id: i64) -> Result<Option<Self>> { pub async fn from_id(pool: &PgPool, id: i64) -> Result<Option<Self>> {
match sqlx::query_as!( match sqlx::query_as!(
Self, Self,
r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE id = $1;"#, r#"SELECT
id,
username,
email,
salt,
password,
joined,
verified as "verified!: bool",
follows,
followers,
permissions
FROM Accounts WHERE id = $1;"#,
id id
) )
.fetch_one(pool) .fetch_one(pool)
@ -84,7 +109,18 @@ impl Account {
pub async fn from_email(pool: &PgPool, email: &String) -> Result<Option<Self>> { pub async fn from_email(pool: &PgPool, email: &String) -> Result<Option<Self>> {
match sqlx::query_as!( match sqlx::query_as!(
Self, Self,
r#"SELECT id, username, email, salt, password, joined, verified as "verified!: bool" FROM Accounts WHERE email = $1;"#, r#"SELECT
id,
username,
email,
salt,
password,
joined,
verified as "verified!: bool",
follows,
followers,
permissions
FROM Accounts WHERE email = $1;"#,
email email
) )
.fetch_one(pool) .fetch_one(pool)
@ -105,4 +141,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,19 +41,34 @@ 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
.await? if let Some(account) = Account::from_username(pool, &request.username).await? {
.is_some() // Check if the account that has taken the username is verified or has an open verification request.
{ if account.verified
return Ok(data::RegisterResponse::Conflict( || VerificationToken::from_id(pool, account.id)
data::RegisterConflict::Username, .await?
)); .is_some()
{
return Ok(data::RegisterResponse::Conflict(
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
return Ok(data::RegisterResponse::Conflict( if let Some(account) = Account::from_email(pool, &request.email).await? {
data::RegisterConflict::Email, if account.verified
)); || VerificationToken::from_id(pool, account.id)
.await?
.is_some()
{
return Ok(data::RegisterResponse::Conflict(
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

@ -15,13 +15,16 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
sqlx::query!( sqlx::query!(
r#" r#"
CREATE TABLE IF NOT EXISTS Accounts ( CREATE TABLE IF NOT EXISTS Accounts (
id SERIAL8 NOT NULL, id SERIAL8 NOT NULL,
username VARCHAR(32) NOT NULL, username VARCHAR(32) NOT NULL,
email TEXT NOT NULL, email TEXT NOT NULL,
salt VARCHAR(22) NOT NULL, salt VARCHAR(22) NOT NULL,
password VARCHAR(96) NOT NULL, password VARCHAR(96) NOT NULL,
joined TIMESTAMP NOT NULL, joined TIMESTAMP NOT NULL,
verified BOOLEAN NOT NULL, verified BOOLEAN NOT NULL,
follows BIGINT[],
followers BIGINT[],
permissions BIGINT NOT NULL,
PRIMARY KEY(id) PRIMARY KEY(id)
); );
"# "#
@ -53,6 +56,21 @@ pub async fn start(port: u16, pool: PgPool) -> Result<()> {
.execute(&pool) .execute(&pool)
.await?; .await?;
sqlx::query!(
r#"
CREATE TABLE IF NOT EXISTS Projects (
id SERIAL8 NOT NULL,
name VARCHAR(32) NOT NULL,
desription TEXT,
created TIMESTAMP NOT NULL,
members BIGINT[][] NOT NULL,
PRIMARY KEY(id)
);
"#
)
.execute(&pool)
.await?;
let _ = HttpServer::new(move || { let _ = HttpServer::new(move || {
App::new() App::new()
.service(account::calls::register) .service(account::calls::register)

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;"#,