From fb379ac69f2534850ff06ce04d6952cb766d29ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Thu, 30 Jul 2026 10:12:10 +0200 Subject: [PATCH] new finctions and fixes --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/api/files.rs | 48 +++++++++++++++++++++++++++++++++++------ src/security.rs | 9 ++++++++ static/css/styles.css | 2 +- static/editor.html | 25 ++++++++++++--------- static/home.html | 2 +- static/js/note-files.js | 5 ++++- 8 files changed, 74 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b56c77..2e45ee5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2581,7 +2581,7 @@ dependencies = [ [[package]] name = "rustpad" -version = "0.2.4" +version = "0.2.6" dependencies = [ "argon2", "aws-config", diff --git a/Cargo.toml b/Cargo.toml index b884e9e..19527e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustpad" -version = "0.2.5" +version = "0.2.6" edition = "2024" rust-version = "1.94" description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL" diff --git a/src/api/files.rs b/src/api/files.rs index e96060a..8e953fa 100644 --- a/src/api/files.rs +++ b/src/api/files.rs @@ -16,7 +16,6 @@ pub async fn upload_pad_file( Path(slug): Path, mut multipart: Multipart, ) -> Result, ApiError> { - require_authenticated_upload(&state, &headers).await?; let mut password: Option = None; let mut access_token: Option = None; let mut file: Option<(String, Vec)> = None; @@ -61,6 +60,14 @@ pub async fn upload_pad_file( &headers, ) .await?; + require_upload_permission( + &state, + &headers, + "pad", + &slug, + resource_request_token(&headers, "pad", &slug, access_token.as_deref()), + ) + .await?; let level = if db::verify_pad_password(&pad, password.as_deref()) || (pad.is_private == 0 && pad.password_hash.is_none()) { @@ -205,7 +212,6 @@ pub async fn upload_note_file( Path((workspace_slug, note_slug)): Path<(String, String)>, mut multipart: Multipart, ) -> Result, ApiError> { - require_authenticated_upload(&state, &headers).await?; let mut password: Option = None; let mut access_token: Option = None; let mut file: Option<(String, Vec)> = None; @@ -252,6 +258,20 @@ pub async fn upload_note_file( ) .await?; + require_upload_permission( + &state, + &headers, + "workspace", + &workspace_slug, + resource_request_token( + &headers, + "workspace", + &workspace_slug, + access_token.as_deref(), + ), + ) + .await?; + let level = if db::verify_workspace_password(&workspace, password.as_deref()) || (workspace.is_private == 0 && workspace.password_hash.is_none()) { @@ -438,17 +458,33 @@ pub async fn delete_note_file( Ok(Json(serde_json::json!({"ok": true}))) } -async fn require_authenticated_upload( +async fn require_upload_permission( state: &SharedState, headers: &HeaderMap, + kind: &str, + slug: &str, + resource_token: Option<&str>, ) -> Result<(), ApiError> { + let permission = crate::auth::share_link_permission(state, kind, slug, resource_token) + .await + .map_err(|error| ApiError::forbidden(&error.message))?; + if permission.as_deref() == Some("rw") { + return Ok(()); + } + let user = crate::auth::optional_user(state, headers) .await .map_err(|error| ApiError::forbidden(&error.message))?; - if user.is_none() { - return Err(ApiError::forbidden("Log in to upload files.")); + if user.is_some() { + return Ok(()); + } + + match permission.as_deref() { + Some("ro") => Err(ApiError::forbidden("Read-only access.")), + _ => Err(ApiError::forbidden( + "Log in or use a read-write share link to upload files.", + )), } - Ok(()) } pub async fn download_file( diff --git a/src/security.rs b/src/security.rs index 65118f9..62ed671 100644 --- a/src/security.rs +++ b/src/security.rs @@ -1,3 +1,12 @@ +/* + * Copyright (C) 2026 Mateusz GruszczyƄski @linuxiarz.pl + * Source-Available Code / Dual-Licensed. + * + * Free for non-commercial and evaluation use under terms of BSL/GPLv3. + * Commercial or production use requires a valid paid license. + * See LICENSE file in repository root for details. + */ + use axum::http::{HeaderMap, HeaderValue, Uri, header}; use sha2::{Digest, Sha256}; diff --git a/static/css/styles.css b/static/css/styles.css index 0377447..b8d4d6f 100644 --- a/static/css/styles.css +++ b/static/css/styles.css @@ -5433,4 +5433,4 @@ dialog::backdrop { } -} +} \ No newline at end of file diff --git a/static/editor.html b/static/editor.html index 7d6d518..8c21e1e 100644 --- a/static/editor.html +++ b/static/editor.html @@ -125,8 +125,9 @@ aria-hidden="true">Colors on
+ type="button" data-authorship-mode="simple" class="active" + disabled>Simple
@@ -218,9 +219,9 @@ guest