Started work on permissions

This commit is contained in:
StNicolay 2024-07-27 21:48:26 +03:00
parent 3ed30f0d9b
commit ffbb2a8bbc
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
8 changed files with 125 additions and 28 deletions

6
build.rs Normal file
View File

@ -0,0 +1,6 @@
// generated by `sqlx migrate build-script`
fn main() {
// trigger recompilation when a new migration is added
println!("cargo:rerun-if-changed=migrations");
println!("cargo:rerun-if-changed=sql");
}

View File

@ -0,0 +1,8 @@
SELECT
username,
permission_type as "permission_type: PermissionRaw"
FROM
permissions
INNER JOIN users ON permissions.user_id = users.user_id
WHERE
folder_id = $1

View File

@ -0,0 +1,15 @@
SELECT
permission_type as "permission_type: PermissionRaw"
FROM
permissions
WHERE
folder_id = $1
AND user_id = $2
UNION
SELECT
'manage' as "permission_type: PermissionRaw"
FROM
folders
WHERE
folder_id = $1
AND owner_id = $2

24
sql/insert_permission.sql Normal file
View File

@ -0,0 +1,24 @@
WITH RECURSIVE folder_hierarchy AS (
-- Start with the given directory
SELECT
folder_id
FROM
folders
WHERE
folder_id = $1
UNION ALL
-- Recursively find all subdirectories
SELECT
f.folder_id
FROM
folders f
INNER JOIN
folder_hierarchy fh ON f.parent_folder_id = fh.folder_id
)
INSERT INTO permissions(user_id, folder_id, permission_type)
SELECT $2::integer as user_id, fh.folder_id::UUID as folder_id, $3
FROM folder_hierarchy fh
ON CONFLICT (user_id, folder_id) DO UPDATE
SET permission_type = $3

View File

@ -47,19 +47,23 @@ pub async fn get_files(folder_id: Uuid, pool: &Pool) -> sqlx::Result<Vec<FileWit
.await .await
} }
async fn get_folder_id(file_id: Uuid, pool: &Pool) -> sqlx::Result<Option<Uuid>> {
let uuid = sqlx::query!("SELECT folder_id FROM files WHERE file_id = $1", file_id)
.fetch_optional(pool)
.await?
.map(|record| record.folder_id);
Ok(uuid)
}
pub async fn get_permissions( pub async fn get_permissions(
file_id: Uuid, file_id: Uuid,
user_id: i32, user_id: i32,
pool: &Pool, pool: &Pool,
) -> sqlx::Result<PermissionType> { ) -> sqlx::Result<PermissionType> {
let record = sqlx::query!( match get_folder_id(file_id, pool).await? {
"SELECT file_id FROM files JOIN folders ON files.folder_id = folders.folder_id WHERE file_id = $1 AND owner_id = $2", Some(folder_id) => super::folder::get_permissions(folder_id, user_id, pool).await,
file_id, None => Ok(PermissionType::default()),
user_id }
)
.fetch_optional(pool)
.await?;
Ok(record.map(|_| PermissionType::Write).unwrap_or_default())
} }
pub async fn get_name(file_id: Uuid, pool: &Pool) -> sqlx::Result<Option<String>> { pub async fn get_name(file_id: Uuid, pool: &Pool) -> sqlx::Result<Option<String>> {

View File

@ -3,7 +3,7 @@ use std::collections::HashSet;
use futures::TryStreamExt; use futures::TryStreamExt;
use uuid::Uuid; use uuid::Uuid;
use crate::Pool; use crate::{db::permissions::PermissionRaw, Pool};
use super::permissions::PermissionType; use super::permissions::PermissionType;
@ -12,16 +12,11 @@ pub async fn get_permissions(
user_id: i32, user_id: i32,
pool: &Pool, pool: &Pool,
) -> sqlx::Result<PermissionType> { ) -> sqlx::Result<PermissionType> {
let permission = sqlx::query!( let permission = sqlx::query_file!("sql/get_permissions_for_folder.sql", folder_id, user_id)
"SELECT folder_id FROM folders WHERE folder_id = $1 AND owner_id = $2", .fetch_optional(pool)
folder_id, .await?
user_id .and_then(|record| record.permission_type);
) Ok(permission.into())
.fetch_optional(pool)
.await?
.map(|_| PermissionType::Write)
.unwrap_or_default();
Ok(permission)
} }
pub async fn get_names(folder_id: Uuid, pool: &Pool) -> sqlx::Result<HashSet<String>> { pub async fn get_names(folder_id: Uuid, pool: &Pool) -> sqlx::Result<HashSet<String>> {

View File

@ -1,9 +1,16 @@
use axum::http::StatusCode; use std::collections::HashMap;
#[derive(sqlx::Type)] use axum::http::StatusCode;
#[sqlx(type_name = "permission", rename_all = "lowercase")] use futures::TryStreamExt as _;
pub(super) enum PermissionRaw { use uuid::Uuid;
Read,
use crate::Pool;
#[derive(sqlx::Type, Debug)]
#[sqlx(type_name = "permission")]
#[sqlx(rename_all = "lowercase")]
pub enum PermissionRaw {
Read = 1,
Write, Write,
Manage, Manage,
} }
@ -29,6 +36,17 @@ impl From<Option<PermissionRaw>> for PermissionType {
} }
} }
impl From<PermissionType> for PermissionRaw {
fn from(value: PermissionType) -> Self {
match value {
PermissionType::Manage => Self::Manage,
PermissionType::Write => Self::Write,
PermissionType::Read => Self::Read,
PermissionType::NoPermission => unreachable!(),
}
}
}
impl PermissionType { impl PermissionType {
pub fn can_read(self) -> bool { pub fn can_read(self) -> bool {
self >= PermissionType::Read self >= PermissionType::Read
@ -57,3 +75,32 @@ impl PermissionType {
Ok(()) Ok(())
} }
} }
pub async fn insert_permissions(
user_id: i32,
folder_id: Uuid,
permission_type: PermissionType,
pool: &Pool,
) -> sqlx::Result<()> {
let permission_type = PermissionRaw::from(permission_type);
sqlx::query_file!(
"sql/insert_permission.sql",
folder_id,
user_id,
permission_type as PermissionRaw
)
.execute(pool)
.await
.map(|_| ())
}
pub async fn get_permissions_for_folder(
folder_id: Uuid,
pool: &Pool,
) -> sqlx::Result<HashMap<String, PermissionType>> {
sqlx::query_file!("sql/get_all_permissions_for_folder.sql", folder_id)
.fetch(pool)
.map_ok(|record| (record.username, Some(record.permission_type).into()))
.try_collect()
.await
}

View File

@ -27,14 +27,12 @@ pub async fn modify(
} }
}; };
let Some(mut file) = state let mut file = state
.storage .storage
.write(params.file_id) .write(params.file_id)
.await .await
.handle_internal()? .handle_internal()?
else { .ok_or(StatusCode::NOT_FOUND)?;
return Err(StatusCode::NOT_FOUND);
};
let (hash, size) = match crate::FileStorage::write_to_file(&mut file, &mut field).await { let (hash, size) = match crate::FileStorage::write_to_file(&mut file, &mut field).await {
Ok(values) => values, Ok(values) => values,