From 0f6bfe21117e68d908f2c3b74dfb46e7cac7de62 Mon Sep 17 00:00:00 2001 From: stefiosif Date: Wed, 3 Jun 2026 23:34:29 +0300 Subject: [PATCH] fix(backend): match the authenticated user into get/download/delete operations --- ...10dd68640f6f14e38a4b3342d514804e3de27.json | 14 +++++ ...2f95928849706ecb75fe1cb8b606aa3cf2971.json | 40 ++++++++++++ ...1d7a0d0c4d04857130da327b07821f20cf07c.json | 58 +++++++++++++++++ ...1e1fb8d9b647dbe6ef025dbb13b6417b6e91e.json | 62 +++++++++++++++++++ ...e5fabc8633a63f6c213b61a7e1902baad157b.json | 41 ++++++++++++ ...1ba3a0f3af9273db03691317ab5e8676d61b7.json | 40 ++++++++++++ ...074a737b1627bc9c4cabbf2abe145d842b8f9.json | 42 +++++++++++++ ...3897a12e58a29b0b2e5c814ff8cb392e32fa0.json | 59 ++++++++++++++++++ ...3186407f6a72cc1596f652fcdcd7c7f49562d.json | 59 ++++++++++++++++++ backend/src/model.rs | 33 ++++++---- backend/src/web/routes_file.rs | 15 ++++- 11 files changed, 449 insertions(+), 14 deletions(-) create mode 100644 backend/.sqlx/query-11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27.json create mode 100644 backend/.sqlx/query-24fd2306e40be21dc82660ec3f42f95928849706ecb75fe1cb8b606aa3cf2971.json create mode 100644 backend/.sqlx/query-31e1333f84168ce0ba75c474f2a1d7a0d0c4d04857130da327b07821f20cf07c.json create mode 100644 backend/.sqlx/query-638f29a940b4514c1ebd047975e1e1fb8d9b647dbe6ef025dbb13b6417b6e91e.json create mode 100644 backend/.sqlx/query-65d6e198b36dc758d3da0e29374e5fabc8633a63f6c213b61a7e1902baad157b.json create mode 100644 backend/.sqlx/query-7d78918485f2c98b302a9d4c0801ba3a0f3af9273db03691317ab5e8676d61b7.json create mode 100644 backend/.sqlx/query-89c32ebfbbdf3f7d6fe47becb7b074a737b1627bc9c4cabbf2abe145d842b8f9.json create mode 100644 backend/.sqlx/query-d059130365f7e4b3a6ae49bee073897a12e58a29b0b2e5c814ff8cb392e32fa0.json create mode 100644 backend/.sqlx/query-f703be0699214db15f5d65ea7ec3186407f6a72cc1596f652fcdcd7c7f49562d.json diff --git a/backend/.sqlx/query-11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27.json b/backend/.sqlx/query-11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27.json new file mode 100644 index 0000000..6fddc7b --- /dev/null +++ b/backend/.sqlx/query-11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM sessions WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "11e96cfd8c2736f13ce55975ea910dd68640f6f14e38a4b3342d514804e3de27" +} diff --git a/backend/.sqlx/query-24fd2306e40be21dc82660ec3f42f95928849706ecb75fe1cb8b606aa3cf2971.json b/backend/.sqlx/query-24fd2306e40be21dc82660ec3f42f95928849706ecb75fe1cb8b606aa3cf2971.json new file mode 100644 index 0000000..cd8c8be --- /dev/null +++ b/backend/.sqlx/query-24fd2306e40be21dc82660ec3f42f95928849706ecb75fe1cb8b606aa3cf2971.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT *\n FROM users u\n WHERE u.username = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "password_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "24fd2306e40be21dc82660ec3f42f95928849706ecb75fe1cb8b606aa3cf2971" +} diff --git a/backend/.sqlx/query-31e1333f84168ce0ba75c474f2a1d7a0d0c4d04857130da327b07821f20cf07c.json b/backend/.sqlx/query-31e1333f84168ce0ba75c474f2a1d7a0d0c4d04857130da327b07821f20cf07c.json new file mode 100644 index 0000000..eadd7a3 --- /dev/null +++ b/backend/.sqlx/query-31e1333f84168ce0ba75c474f2a1d7a0d0c4d04857130da327b07821f20cf07c.json @@ -0,0 +1,58 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT *\n FROM file_records fr\n WHERE user_id = $1\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "file_type", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "size", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "uploaded_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "storage_key", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "user_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "31e1333f84168ce0ba75c474f2a1d7a0d0c4d04857130da327b07821f20cf07c" +} diff --git a/backend/.sqlx/query-638f29a940b4514c1ebd047975e1e1fb8d9b647dbe6ef025dbb13b6417b6e91e.json b/backend/.sqlx/query-638f29a940b4514c1ebd047975e1e1fb8d9b647dbe6ef025dbb13b6417b6e91e.json new file mode 100644 index 0000000..1f7e933 --- /dev/null +++ b/backend/.sqlx/query-638f29a940b4514c1ebd047975e1e1fb8d9b647dbe6ef025dbb13b6417b6e91e.json @@ -0,0 +1,62 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO file_records (user_id, name, file_type, size, storage_key)\n VALUES ($1, $2, $3, $4, $5)\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "file_type", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "size", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "uploaded_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "storage_key", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "user_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Text", + "Text", + "Int8", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "638f29a940b4514c1ebd047975e1e1fb8d9b647dbe6ef025dbb13b6417b6e91e" +} diff --git a/backend/.sqlx/query-65d6e198b36dc758d3da0e29374e5fabc8633a63f6c213b61a7e1902baad157b.json b/backend/.sqlx/query-65d6e198b36dc758d3da0e29374e5fabc8633a63f6c213b61a7e1902baad157b.json new file mode 100644 index 0000000..b96f06f --- /dev/null +++ b/backend/.sqlx/query-65d6e198b36dc758d3da0e29374e5fabc8633a63f6c213b61a7e1902baad157b.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO users (username, password_hash)\n VALUES ($1, $2)\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "username", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "password_hash", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "65d6e198b36dc758d3da0e29374e5fabc8633a63f6c213b61a7e1902baad157b" +} diff --git a/backend/.sqlx/query-7d78918485f2c98b302a9d4c0801ba3a0f3af9273db03691317ab5e8676d61b7.json b/backend/.sqlx/query-7d78918485f2c98b302a9d4c0801ba3a0f3af9273db03691317ab5e8676d61b7.json new file mode 100644 index 0000000..6587776 --- /dev/null +++ b/backend/.sqlx/query-7d78918485f2c98b302a9d4c0801ba3a0f3af9273db03691317ab5e8676d61b7.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT *\n FROM sessions s\n WHERE s.id = $1\n AND s.expires_at > NOW()\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "expires_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "7d78918485f2c98b302a9d4c0801ba3a0f3af9273db03691317ab5e8676d61b7" +} diff --git a/backend/.sqlx/query-89c32ebfbbdf3f7d6fe47becb7b074a737b1627bc9c4cabbf2abe145d842b8f9.json b/backend/.sqlx/query-89c32ebfbbdf3f7d6fe47becb7b074a737b1627bc9c4cabbf2abe145d842b8f9.json new file mode 100644 index 0000000..b319a29 --- /dev/null +++ b/backend/.sqlx/query-89c32ebfbbdf3f7d6fe47becb7b074a737b1627bc9c4cabbf2abe145d842b8f9.json @@ -0,0 +1,42 @@ +{ + "db_name": "PostgreSQL", + "query": "\n INSERT INTO sessions (id, user_id, expires_at)\n VALUES ($1, $2, $3)\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Text" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "expires_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 3, + "name": "created_at", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text", + "Int8", + "Timestamptz" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "89c32ebfbbdf3f7d6fe47becb7b074a737b1627bc9c4cabbf2abe145d842b8f9" +} diff --git a/backend/.sqlx/query-d059130365f7e4b3a6ae49bee073897a12e58a29b0b2e5c814ff8cb392e32fa0.json b/backend/.sqlx/query-d059130365f7e4b3a6ae49bee073897a12e58a29b0b2e5c814ff8cb392e32fa0.json new file mode 100644 index 0000000..cb7ec06 --- /dev/null +++ b/backend/.sqlx/query-d059130365f7e4b3a6ae49bee073897a12e58a29b0b2e5c814ff8cb392e32fa0.json @@ -0,0 +1,59 @@ +{ + "db_name": "PostgreSQL", + "query": "\n SELECT *\n FROM file_records fr\n WHERE fr.id = $1\n AND fr.user_id = $2\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "file_type", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "size", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "uploaded_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "storage_key", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "user_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "d059130365f7e4b3a6ae49bee073897a12e58a29b0b2e5c814ff8cb392e32fa0" +} diff --git a/backend/.sqlx/query-f703be0699214db15f5d65ea7ec3186407f6a72cc1596f652fcdcd7c7f49562d.json b/backend/.sqlx/query-f703be0699214db15f5d65ea7ec3186407f6a72cc1596f652fcdcd7c7f49562d.json new file mode 100644 index 0000000..952646a --- /dev/null +++ b/backend/.sqlx/query-f703be0699214db15f5d65ea7ec3186407f6a72cc1596f652fcdcd7c7f49562d.json @@ -0,0 +1,59 @@ +{ + "db_name": "PostgreSQL", + "query": "\n DELETE FROM file_records\n WHERE id = $1\n AND user_id = $2\n RETURNING *\n ", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "name", + "type_info": "Text" + }, + { + "ordinal": 2, + "name": "file_type", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "size", + "type_info": "Int8" + }, + { + "ordinal": 4, + "name": "uploaded_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 5, + "name": "storage_key", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "user_id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "f703be0699214db15f5d65ea7ec3186407f6a72cc1596f652fcdcd7c7f49562d" +} diff --git a/backend/src/model.rs b/backend/src/model.rs index 83bf63f..b617217 100644 --- a/backend/src/model.rs +++ b/backend/src/model.rs @@ -106,12 +106,13 @@ impl FileRepository { pub async fn download_file( &self, file_id: i64, + user_id: i64, ) -> Result> + use<>, LoftError> { info!( "Fetching metadata of file \"{}\" from file_records", file_id ); - let record = self.get_file(file_id).await?; + let record = self.get_file(file_id, user_id).await?; info!("Downloading file \"{}\"", file_id); let reader = self.op.reader(&record.storage_key).await.unwrap(); let stream = reader.into_bytes_stream(0..).await.unwrap(); @@ -119,7 +120,7 @@ impl FileRepository { Ok(stream) } - pub async fn get_file(&self, file_id: i64) -> Result { + pub async fn get_file(&self, file_id: i64, user_id: i64) -> Result { info!( "Fetching metadata of file \"{}\" from file_records", file_id @@ -130,8 +131,10 @@ impl FileRepository { SELECT * FROM file_records fr WHERE fr.id = $1 + AND fr.user_id = $2 "#, - file_id + file_id, + user_id ) .fetch_optional(&self.pool) .await @@ -141,12 +144,12 @@ impl FileRepository { Ok(record) } - pub async fn delete_file(&self, file_id: i64) -> Result { + pub async fn delete_file(&self, file_id: i64, user_id: i64) -> Result { info!( "Fetching metadata of file \"{}\" from file_records", file_id ); - let record = self.get_file(file_id).await?; + let record = self.get_file(file_id, user_id).await?; info!("Deleting file bytes \"{}\"", file_id); self.op.delete(&record.storage_key).await.unwrap(); @@ -156,9 +159,11 @@ impl FileRepository { r#" DELETE FROM file_records WHERE id = $1 + AND user_id = $2 RETURNING * "#, - file_id + file_id, + user_id ) .fetch_optional(&self.pool) .await @@ -407,7 +412,10 @@ mod tests { .await .unwrap(); - let _downloaded = file_repository.download_file(file_record.id).await.unwrap(); + let _downloaded = file_repository + .download_file(file_record.id, user.id) + .await + .unwrap(); //TODO: add assert @@ -422,7 +430,7 @@ mod tests { truncate_file_records(&file_repository.pool).await; assert!(matches!( - file_repository.download_file(i64::MAX).await, + file_repository.download_file(i64::MAX, 1).await, Err(LoftError::FileIdNotFound) )); } @@ -453,10 +461,13 @@ mod tests { .await .unwrap(); - file_repository.delete_file(file_record.id).await.unwrap(); + file_repository + .delete_file(file_record.id, user.id) + .await + .unwrap(); assert!(matches!( - file_repository.download_file(file_record.id).await, + file_repository.download_file(file_record.id, user.id).await, Err(LoftError::FileIdNotFound) )); truncate_file_records(&file_repository.pool).await; @@ -469,7 +480,7 @@ mod tests { let file_repository = file_repository().await.unwrap(); assert!(matches!( - file_repository.delete_file(99).await, + file_repository.delete_file(99, 1).await, Err(LoftError::FileIdNotFound) )); } diff --git a/backend/src/web/routes_file.rs b/backend/src/web/routes_file.rs index 4b7d20c..7c11231 100644 --- a/backend/src/web/routes_file.rs +++ b/backend/src/web/routes_file.rs @@ -57,28 +57,37 @@ async fn upload_file( #[axum::debug_handler] async fn get_file( State(file_repository): State, + ctx: Ctx, Path(file_id): Path, ) -> Result, LoftError> { - let record = file_repository.get_file(file_id as i64).await?; + let record = file_repository + .get_file(file_id as i64, ctx.user_id()) + .await?; Ok(Json(record)) } #[axum::debug_handler] async fn download_file( State(file_repository): State, + ctx: Ctx, Path(file_id): Path, ) -> Result { - let stream = file_repository.download_file(file_id as i64).await?; + let stream = file_repository + .download_file(file_id as i64, ctx.user_id()) + .await?; Ok(Body::from_stream(stream)) } async fn delete_file( State(file_repository): State, + ctx: Ctx, Path(file_id): Path, ) -> Result, LoftError> { info!("handler: delete_file"); - let file = file_repository.delete_file(file_id as i64).await?; + let file = file_repository + .delete_file(file_id as i64, ctx.user_id()) + .await?; Ok(Json(file)) }