Permissions

This commit is contained in:
StNicolay 2024-07-27 22:08:56 +03:00
parent ffbb2a8bbc
commit 5d57c936a7
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
10 changed files with 133 additions and 11 deletions

View File

@ -32,5 +32,6 @@ CREATE TABLE
permission_id SERIAL PRIMARY KEY, permission_id SERIAL PRIMARY KEY,
user_id INT REFERENCES users (user_id) ON DELETE CASCADE NOT NULL, user_id INT REFERENCES users (user_id) ON DELETE CASCADE NOT NULL,
folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE, folder_id UUID REFERENCES folders (folder_id) ON DELETE CASCADE,
permission_type permission NOT NULL permission_type permission NOT NULL,
UNIQUE (user_id, folder_id)
); );

View File

@ -0,0 +1,20 @@
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
)
DELETE FROM permissions WHERE user_id = $2 AND folder_id IN (SELECT folder_id FROM folder_hierarchy)

View File

@ -36,13 +36,13 @@ pub struct FileWithoutParentId {
file_id: Uuid, file_id: Uuid,
file_name: String, file_name: String,
file_size: i64, file_size: i64,
sha512: Vec<u8>, sha512: String,
created_at: chrono::NaiveDateTime, created_at: chrono::NaiveDateTime,
updated_at: chrono::NaiveDateTime, updated_at: chrono::NaiveDateTime,
} }
pub async fn get_files(folder_id: Uuid, pool: &Pool) -> sqlx::Result<Vec<FileWithoutParentId>> { pub async fn get_files(folder_id: Uuid, pool: &Pool) -> sqlx::Result<Vec<FileWithoutParentId>> {
sqlx::query_as!(FileWithoutParentId, "SELECT file_id, file_name, file_size, sha512, created_at, updated_at FROM files WHERE folder_id = $1", folder_id) sqlx::query_as!(FileWithoutParentId, r#"SELECT file_id, file_name, file_size, encode(sha512, 'base64') as "sha512!", created_at, updated_at FROM files WHERE folder_id = $1"#, folder_id)
.fetch_all(pool) .fetch_all(pool)
.await .await
} }

View File

@ -2,11 +2,12 @@ use std::collections::HashMap;
use axum::http::StatusCode; use axum::http::StatusCode;
use futures::TryStreamExt as _; use futures::TryStreamExt as _;
use serde::{Deserialize, Serialize};
use uuid::Uuid; use uuid::Uuid;
use crate::Pool; use crate::Pool;
#[derive(sqlx::Type, Debug)] #[derive(sqlx::Type, Debug, Serialize, Deserialize)]
#[sqlx(type_name = "permission")] #[sqlx(type_name = "permission")]
#[sqlx(rename_all = "lowercase")] #[sqlx(rename_all = "lowercase")]
pub enum PermissionRaw { pub enum PermissionRaw {
@ -76,13 +77,12 @@ impl PermissionType {
} }
} }
pub async fn insert_permissions( pub async fn insert(
user_id: i32, user_id: i32,
folder_id: Uuid, folder_id: Uuid,
permission_type: PermissionType, permission_type: PermissionRaw,
pool: &Pool, pool: &Pool,
) -> sqlx::Result<()> { ) -> sqlx::Result<()> {
let permission_type = PermissionRaw::from(permission_type);
sqlx::query_file!( sqlx::query_file!(
"sql/insert_permission.sql", "sql/insert_permission.sql",
folder_id, folder_id,
@ -94,13 +94,20 @@ pub async fn insert_permissions(
.map(|_| ()) .map(|_| ())
} }
pub async fn get_permissions_for_folder( pub async fn get_all_for_folder(
folder_id: Uuid, folder_id: Uuid,
pool: &Pool, pool: &Pool,
) -> sqlx::Result<HashMap<String, PermissionType>> { ) -> sqlx::Result<HashMap<String, PermissionRaw>> {
sqlx::query_file!("sql/get_all_permissions_for_folder.sql", folder_id) sqlx::query_file!("sql/get_all_permissions_for_folder.sql", folder_id)
.fetch(pool) .fetch(pool)
.map_ok(|record| (record.username, Some(record.permission_type).into())) .map_ok(|record| (record.username, record.permission_type))
.try_collect() .try_collect()
.await .await
} }
pub async fn delete_for_folder(folder_id: Uuid, user_id: i32, pool: &Pool) -> sqlx::Result<()> {
sqlx::query_file!("sql/delete_permissions.sql", folder_id, user_id)
.execute(pool)
.await
.map(|_| ())
}

View File

@ -1,2 +1,3 @@
pub mod file; pub mod file;
pub mod folder; pub mod folder;
pub mod permissions;

View File

@ -0,0 +1,26 @@
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
folder_id: Uuid,
user_id: i32,
}
pub async fn delete(
claims: Claims,
State(state): State<AppState>,
Json(params): Json<Params>,
) -> Result<StatusCode, StatusCode> {
if params.user_id != claims.user_id {
db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
.await
.handle_internal()?
.can_manage_guard()?;
}
db::permissions::delete_for_folder(params.folder_id, params.user_id, &state.pool)
.await
.handle_internal()?;
Ok(StatusCode::NO_CONTENT)
}

View File

@ -0,0 +1,26 @@
use std::collections::HashMap;
use db::permissions::PermissionRaw;
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
folder_id: Uuid,
}
pub async fn get(
Query(params): Query<Params>,
claims: Claims,
State(state): State<AppState>,
) -> Result<Json<HashMap<String, PermissionRaw>>, StatusCode> {
db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
.await
.handle_internal()?
.can_manage_guard()?;
let permissions = db::permissions::get_all_for_folder(params.folder_id, &state.pool)
.await
.handle_internal()?;
Ok(Json(permissions))
}

View File

@ -0,0 +1,3 @@
pub mod delete;
pub mod get;
pub mod set;

View File

@ -0,0 +1,32 @@
use db::permissions::PermissionRaw;
use crate::prelude::*;
#[derive(Deserialize, Debug)]
pub struct Params {
folder_id: Uuid,
permission_type: PermissionRaw,
user_id: i32,
}
pub async fn set(
claims: Claims,
State(state): State<AppState>,
Json(params): Json<Params>,
) -> Result<StatusCode, StatusCode> {
db::folder::get_permissions(params.folder_id, claims.user_id, &state.pool)
.await
.handle_internal()?
.can_manage_guard()?;
db::permissions::insert(
params.user_id,
params.folder_id,
params.permission_type,
&state.pool,
)
.await
.handle_internal()?;
Ok(StatusCode::NO_CONTENT)
}

View File

@ -85,7 +85,7 @@ async fn main() -> anyhow::Result<()> {
fn app(state: AppState) -> Router { fn app(state: AppState) -> Router {
use axum::http::header; use axum::http::header;
use endpoints::{file, folder}; use endpoints::{file, folder, permissions};
use tower_http::ServiceBuilderExt as _; use tower_http::ServiceBuilderExt as _;
let sensitive_headers = [header::AUTHORIZATION, header::COOKIE]; let sensitive_headers = [header::AUTHORIZATION, header::COOKIE];
@ -110,6 +110,12 @@ fn app(state: AppState) -> Router {
.post(folder::create::create) .post(folder::create::create)
.delete(folder::delete::delete), .delete(folder::delete::delete),
) )
.route(
"/permissions",
get(permissions::get::get)
.post(permissions::set::set)
.delete(permissions::delete::delete),
)
.layer(middleware) .layer(middleware)
.with_state(state) .with_state(state)
} }