From ffbb2a8bbc23cfe62d0b511ccd673421285210b5 Mon Sep 17 00:00:00 2001 From: StNicolay Date: Sat, 27 Jul 2024 21:48:26 +0300 Subject: [PATCH] Started work on permissions --- build.rs | 6 +++ sql/get_all_permissions_for_folder.sql | 8 ++++ sql/get_permissions_for_folder.sql | 15 +++++++ sql/insert_permission.sql | 24 +++++++++++ src/db/file.rs | 20 +++++---- src/db/folder.rs | 17 +++----- src/db/permissions.rs | 57 +++++++++++++++++++++++--- src/endpoints/file/modify.rs | 6 +-- 8 files changed, 125 insertions(+), 28 deletions(-) create mode 100644 build.rs create mode 100644 sql/get_all_permissions_for_folder.sql create mode 100644 sql/get_permissions_for_folder.sql create mode 100644 sql/insert_permission.sql diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..82b446c --- /dev/null +++ b/build.rs @@ -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"); +} diff --git a/sql/get_all_permissions_for_folder.sql b/sql/get_all_permissions_for_folder.sql new file mode 100644 index 0000000..1dd1345 --- /dev/null +++ b/sql/get_all_permissions_for_folder.sql @@ -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 \ No newline at end of file diff --git a/sql/get_permissions_for_folder.sql b/sql/get_permissions_for_folder.sql new file mode 100644 index 0000000..3282f34 --- /dev/null +++ b/sql/get_permissions_for_folder.sql @@ -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 \ No newline at end of file diff --git a/sql/insert_permission.sql b/sql/insert_permission.sql new file mode 100644 index 0000000..89787a3 --- /dev/null +++ b/sql/insert_permission.sql @@ -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 \ No newline at end of file diff --git a/src/db/file.rs b/src/db/file.rs index 62058b3..9a2ee8d 100644 --- a/src/db/file.rs +++ b/src/db/file.rs @@ -47,19 +47,23 @@ pub async fn get_files(folder_id: Uuid, pool: &Pool) -> sqlx::Result sqlx::Result> { + 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( file_id: Uuid, user_id: i32, pool: &Pool, ) -> sqlx::Result { - let record = sqlx::query!( - "SELECT file_id FROM files JOIN folders ON files.folder_id = folders.folder_id WHERE file_id = $1 AND owner_id = $2", - file_id, - user_id - ) - .fetch_optional(pool) - .await?; - Ok(record.map(|_| PermissionType::Write).unwrap_or_default()) + match get_folder_id(file_id, pool).await? { + Some(folder_id) => super::folder::get_permissions(folder_id, user_id, pool).await, + None => Ok(PermissionType::default()), + } } pub async fn get_name(file_id: Uuid, pool: &Pool) -> sqlx::Result> { diff --git a/src/db/folder.rs b/src/db/folder.rs index a706289..c3ad2dc 100644 --- a/src/db/folder.rs +++ b/src/db/folder.rs @@ -3,7 +3,7 @@ use std::collections::HashSet; use futures::TryStreamExt; use uuid::Uuid; -use crate::Pool; +use crate::{db::permissions::PermissionRaw, Pool}; use super::permissions::PermissionType; @@ -12,16 +12,11 @@ pub async fn get_permissions( user_id: i32, pool: &Pool, ) -> sqlx::Result { - let permission = sqlx::query!( - "SELECT folder_id FROM folders WHERE folder_id = $1 AND owner_id = $2", - folder_id, - user_id - ) - .fetch_optional(pool) - .await? - .map(|_| PermissionType::Write) - .unwrap_or_default(); - Ok(permission) + let permission = sqlx::query_file!("sql/get_permissions_for_folder.sql", folder_id, user_id) + .fetch_optional(pool) + .await? + .and_then(|record| record.permission_type); + Ok(permission.into()) } pub async fn get_names(folder_id: Uuid, pool: &Pool) -> sqlx::Result> { diff --git a/src/db/permissions.rs b/src/db/permissions.rs index dbfa352..1efb22e 100644 --- a/src/db/permissions.rs +++ b/src/db/permissions.rs @@ -1,9 +1,16 @@ -use axum::http::StatusCode; +use std::collections::HashMap; -#[derive(sqlx::Type)] -#[sqlx(type_name = "permission", rename_all = "lowercase")] -pub(super) enum PermissionRaw { - Read, +use axum::http::StatusCode; +use futures::TryStreamExt as _; +use uuid::Uuid; + +use crate::Pool; + +#[derive(sqlx::Type, Debug)] +#[sqlx(type_name = "permission")] +#[sqlx(rename_all = "lowercase")] +pub enum PermissionRaw { + Read = 1, Write, Manage, } @@ -29,6 +36,17 @@ impl From> for PermissionType { } } +impl From 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 { pub fn can_read(self) -> bool { self >= PermissionType::Read @@ -57,3 +75,32 @@ impl PermissionType { 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> { + 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 +} diff --git a/src/endpoints/file/modify.rs b/src/endpoints/file/modify.rs index 08852df..a40a5bb 100644 --- a/src/endpoints/file/modify.rs +++ b/src/endpoints/file/modify.rs @@ -27,14 +27,12 @@ pub async fn modify( } }; - let Some(mut file) = state + let mut file = state .storage .write(params.file_id) .await .handle_internal()? - else { - return Err(StatusCode::NOT_FOUND); - }; + .ok_or(StatusCode::NOT_FOUND)?; let (hash, size) = match crate::FileStorage::write_to_file(&mut file, &mut field).await { Ok(values) => values,