security upgrade

This commit is contained in:
Mateusz Gruszczyński
2026-07-30 09:57:43 +02:00
parent 2274cf57c9
commit f6b8e016a8
24 changed files with 1100 additions and 272 deletions
+47 -18
View File
@@ -94,10 +94,10 @@ pub async fn pad_info(
.ok_or_else(ApiError::not_found_note)?;
ensure_private_resource_access(
&state,
&headers,
"pad",
&pad.slug,
pad.is_private,
bearer_token(&headers),
)
.await?;
let (global_color, note_color) = editor_colors(&state, &headers, "pad", &slug).await?;
@@ -238,8 +238,9 @@ pub async fn publish_pad_page(
&state,
&slug,
payload.password.as_deref(),
payload.access_token.as_deref(),
resource_request_token(&headers, "pad", &slug, payload.access_token.as_deref()),
bearer_token(&headers),
&headers,
)
.await?;
let level = if db::verify_pad_password(&pad, payload.password.as_deref())
@@ -251,7 +252,7 @@ pub async fn publish_pad_page(
&state,
"pad",
&slug,
payload.access_token.as_deref(),
resource_request_token(&headers, "pad", &slug, payload.access_token.as_deref()),
bearer_token(&headers),
)
.await?
@@ -287,8 +288,14 @@ pub async fn publish_note_page(
&workspace_slug,
&note_slug,
payload.password.as_deref(),
payload.access_token.as_deref(),
resource_request_token(
&headers,
"workspace",
&workspace_slug,
payload.access_token.as_deref(),
),
bearer_token(&headers),
&headers,
)
.await?;
let level = if db::verify_workspace_password(&workspace, payload.password.as_deref())
@@ -300,7 +307,12 @@ pub async fn publish_note_page(
&state,
"workspace",
&workspace_slug,
payload.access_token.as_deref(),
resource_request_token(
&headers,
"workspace",
&workspace_slug,
payload.access_token.as_deref(),
),
bearer_token(&headers),
)
.await?
@@ -339,7 +351,6 @@ async fn ensure_public_page_access(
page: &db::PublishedPage,
) -> Result<(), ApiError> {
let password = page_password(headers);
let bearer = bearer_token(headers);
if let Some(pad_id) = page.pad_id {
if db::pad_public_page_unprotected(&state.db, pad_id).await? {
return Ok(());
@@ -358,10 +369,13 @@ async fn ensure_public_page_access(
let pad = db::find_pad(&state.db, &slug)
.await?
.ok_or_else(ApiError::not_found_note)?;
if db::verify_pad_password(&pad, password) {
if has_header_resource_access(state, headers, "pad", &slug).await? {
return Ok(());
}
if verify_resource_access_token(state, "pad", &slug, bearer).await? {
let password_ok = db::verify_pad_password(&pad, password);
check_resource_password_attempt(state, headers, "pad", &slug, password, password_ok)
.await?;
if password_ok {
return Ok(());
}
return if pad.password_hash.is_some() {
@@ -392,10 +406,20 @@ async fn ensure_public_page_access(
let workspace = db::find_workspace(&state.db, &slug)
.await?
.ok_or_else(ApiError::not_found_workspace)?;
if db::verify_workspace_password(&workspace, password) {
if has_header_resource_access(state, headers, "workspace", &slug).await? {
return Ok(());
}
if verify_resource_access_token(state, "workspace", &slug, bearer).await? {
let password_ok = db::verify_workspace_password(&workspace, password);
check_resource_password_attempt(
state,
headers,
"workspace",
&slug,
password,
password_ok,
)
.await?;
if password_ok {
return Ok(());
}
return if workspace.password_hash.is_some() {
@@ -476,8 +500,9 @@ pub async fn pad_history(
&state,
&slug,
payload.password.as_deref(),
payload.access_token.as_deref(),
resource_request_token(&headers, "pad", &slug, payload.access_token.as_deref()),
bearer_token(&headers),
&headers,
)
.await?;
let revisions = db::list_pad_revisions(&state.db, pad.id)
@@ -501,8 +526,9 @@ pub async fn pad_restore(
&state,
&slug,
payload.password.as_deref(),
payload.access_token.as_deref(),
resource_request_token(&headers, "pad", &slug, payload.access_token.as_deref()),
bearer_token(&headers),
&headers,
)
.await?;
let level = if db::verify_pad_password(&pad, payload.password.as_deref())
@@ -514,7 +540,7 @@ pub async fn pad_restore(
&state,
"pad",
&slug,
payload.access_token.as_deref(),
resource_request_token(&headers, "pad", &slug, payload.access_token.as_deref()),
bearer_token(&headers),
)
.await?
@@ -555,6 +581,7 @@ pub(super) async fn authorized_pad(
password: Option<&str>,
access_token: Option<&str>,
bearer: Option<&str>,
headers: &HeaderMap,
) -> Result<db::Pad, ApiError> {
let pad = db::find_pad(&state.db, slug)
.await?
@@ -563,11 +590,13 @@ pub(super) async fn authorized_pad(
if pad.is_private != 0 && token_level == AccessLevel::None {
return Err(ApiError::not_found_note());
}
if pad.password_hash.is_some()
&& !db::verify_pad_password(&pad, password)
&& token_level == AccessLevel::None
{
return Err(ApiError::forbidden("Password required or incorrect."));
if pad.password_hash.is_some() && token_level < AccessLevel::Write {
let password_ok = db::verify_pad_password(&pad, password);
check_resource_password_attempt(state, headers, "pad", slug, password, password_ok)
.await?;
if token_level == AccessLevel::None && !password_ok {
return Err(ApiError::forbidden("Password required or incorrect."));
}
}
Ok(pad)
}