feat(backend): implemented basic permissions

This commit is contained in:
antifallobst 2024-03-20 10:52:05 +01:00
parent c13c70e6e4
commit d9480a8727
Signed by: antifallobst
GPG Key ID: 2B4F402172791BAF
4 changed files with 93 additions and 6 deletions

View File

@ -8,4 +8,5 @@ pub struct ActivationTokensRow {
pub struct UsersRow {
pub userid: String,
pub password: String,
pub permissions: u16,
}

View File

@ -1,5 +1,7 @@
mod db_structures;
pub mod error;
mod permissions;
mod user;
use crate::backend::error::AccountRegisterError;
use crate::config::Config;
@ -11,6 +13,7 @@ use argon2::{
use db_structures::{ActivationTokensRow, UsersRow};
use log::info;
use sqlx::{types::chrono::Utc, MySqlPool};
use user::User;
use uuid::Uuid;
#[derive(Debug, Clone)]
@ -25,7 +28,8 @@ impl Backend {
sqlx::query!(
r#"CREATE TABLE IF NOT EXISTS Users (
userid UUID NOT NULL PRIMARY KEY,
password VARCHAR(128) NOT NULL
password VARCHAR(128) NOT NULL,
permissions SMALLINT UNSIGNED NOT NULL
);"#
)
.execute(&pool)
@ -90,7 +94,7 @@ impl Backend {
}
}
async fn get_user(&self, userid: Uuid) -> Result<Option<()>, sqlx::Error> {
async fn get_user(&self, userid: Uuid) -> Result<Option<User>> {
match sqlx::query_as!(
UsersRow,
r#"SELECT * FROM Users WHERE userid = ?;"#,
@ -101,9 +105,9 @@ impl Backend {
{
Err(e) => match e {
sqlx::Error::RowNotFound => Ok(None),
_ => Err(e),
_ => Err(e.into()),
},
Ok(_row) => Ok(Some(())),
Ok(row) => Ok(Some(row.try_into()?)),
}
}
@ -126,7 +130,36 @@ impl Backend {
let userid = Uuid::new_v4();
sqlx::query!(
r#"INSERT INTO Users VALUES (?, ?);"#,
r#"INSERT INTO Users VALUES (?, ?, 0);"#,
userid.as_bytes().as_slice(),
hash
)
.execute(&self.pool)
.await?;
Ok(Ok(userid))
}
pub async fn create_activation_token(
&self,
token: String,
password: String,
) -> Result<Result<Uuid, error::AccountRegisterError>> {
if !self.check_activation_token(&token).await? {
return Ok(Err(AccountRegisterError::InvalidToken));
}
let salt = SaltString::generate(&mut OsRng);
let hash = Argon2::default()
.hash_password(password.as_bytes(), &salt)
.map_err(|_| anyhow::Error::msg("Failed to hash the password"))?
.to_string();
let userid = Uuid::new_v4();
sqlx::query!(
r#"INSERT INTO Users VALUES (?, ?, 0);"#,
userid.as_bytes().as_slice(),
hash
)

View File

@ -0,0 +1,13 @@
pub enum Permission {
GenerateInviteTokens,
PromoteUsers,
}
impl Permission {
pub fn as_u16(&self) -> u16 {
match self {
Permission::GenerateInviteTokens => 1,
Permission::PromoteUsers => 1 << 1,
}
}
}

40
src/backend/user.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::backend::db_structures::UsersRow;
use crate::backend::{permissions::Permission, Backend};
use anyhow::Result;
use std::str::FromStr;
use uuid::Uuid;
pub struct User {
uuid: Uuid,
password: String,
permissions: u16,
}
impl TryFrom<UsersRow> for User {
type Error = anyhow::Error;
fn try_from(value: UsersRow) -> std::result::Result<Self, Self::Error> {
Ok(Self {
uuid: Uuid::from_str(value.userid.as_str())?,
password: value.password,
permissions: value.permissions,
})
}
}
impl User {
pub fn has_permission(&self, permission: Permission) -> bool {
(self.permissions & permission.as_u16()) > 0
}
async fn flush(&self, backend: &Backend) -> Result<()> {
sqlx::query!(
r#"UPDATE Users SET password = ?, permissions = ? WHERE userid = ?;"#,
self.password,
self.permissions,
self.uuid.as_bytes().as_slice()
)
.execute(&backend.pool)
.await?;
Ok(())
}
}