From 8f68bc600f15502e45a2d9a9764b66817fb6206a Mon Sep 17 00:00:00 2001 From: Eason <30045503+Eason0729@users.noreply.github.com> Date: Fri, 12 Jul 2024 14:13:20 +0000 Subject: [PATCH] feat(backend): add exposing access control to frontend --- backend/src/endpoint/chat.rs | 24 ++++++++++----- backend/src/endpoint/contest.rs | 19 +++++++----- backend/src/endpoint/education.rs | 20 +++++++----- backend/src/endpoint/problem.rs | 51 +++++++++++++++++-------------- backend/src/endpoint/testcase.rs | 19 +++++++----- backend/src/endpoint/user.rs | 18 +++++++++-- grpc/proto/backend.proto | 8 ++++- 7 files changed, 104 insertions(+), 55 deletions(-) diff --git a/backend/src/endpoint/chat.rs b/backend/src/endpoint/chat.rs index d00cfa7..c1578e2 100644 --- a/backend/src/endpoint/chat.rs +++ b/backend/src/endpoint/chat.rs @@ -4,18 +4,23 @@ use grpc::backend::chat_server::*; use crate::entity::chat::{Paginator, *}; -impl From for ChatInfo { - fn from(value: Model) -> Self { +impl<'a> From> for ChatInfo { + fn from(value: WithAuth<'a, Model>) -> Self { + let model = value.1; + let writable = Entity::writable(&model, value.0); ChatInfo { - id: value.id, - user_id: value.user_id, - problem_id: value.problem_id, - create_at: into_prost(value.create_at), - message: value.message, + id: model.id, + user_id: model.user_id, + problem_id: model.problem_id, + create_at: into_prost(model.create_at), + message: model.message, + writable, } } } +impl<'a> WithAuthTrait for Model {} + #[tonic::async_trait] impl Chat for ArcServer { async fn create(&self, req: Request) -> Result, Status> { @@ -103,7 +108,10 @@ impl Chat for ArcServer { let paginator = paginator.into_inner(); Ok(Response::new(ListChatResponse { - list: list.into_iter().map(Into::into).collect(), + list: list + .into_iter() + .map(|x| x.with_auth(&auth).into()) + .collect(), paginator: self.crypto.encode(paginator)?, remain, })) diff --git a/backend/src/endpoint/contest.rs b/backend/src/endpoint/contest.rs index 3bf6200..67616bc 100644 --- a/backend/src/endpoint/contest.rs +++ b/backend/src/endpoint/contest.rs @@ -7,16 +7,21 @@ use crate::entity::{ use grpc::backend::contest_server::*; -impl From for ContestFullInfo { - fn from(value: Model) -> Self { +impl<'a> From> for ContestFullInfo { + fn from(value: WithAuth<'a, Model>) -> Self { + let model = value.1; + let writable = Entity::writable(&model, value.0); ContestFullInfo { - info: value.clone().into(), - content: value.content, - host: value.hoster, + info: model.clone().into(), + content: model.content, + host: model.hoster, + writable, } } } +impl<'a> WithAuthTrait for Model {} + impl From for UserRank { fn from(value: user_contest::Model) -> Self { UserRank { @@ -96,7 +101,7 @@ impl Contest for ArcServer { } #[instrument(skip_all, level = "debug")] async fn full_info(&self, req: Request) -> Result, Status> { - let (_, req) = self + let (auth, req) = self .parse_request_n(req, NonZeroU32!(5)) .in_current_span() .await?; @@ -109,7 +114,7 @@ impl Contest for ArcServer { .map_err(Into::::into)? .ok_or(Error::NotInDB)?; - Ok(Response::new(model.into())) + Ok(Response::new(model.with_auth(&auth).into())) } #[instrument(skip_all, level = "debug")] async fn create(&self, req: Request) -> Result, Status> { diff --git a/backend/src/endpoint/education.rs b/backend/src/endpoint/education.rs index e5a1478..890b051 100644 --- a/backend/src/endpoint/education.rs +++ b/backend/src/endpoint/education.rs @@ -4,18 +4,24 @@ use grpc::backend::education_server::*; use crate::entity::{education::Paginator, education::*, *}; -impl From for EducationFullInfo { - fn from(value: Model) -> Self { +impl<'a> From> for EducationFullInfo { + fn from(value: WithAuth<'a, Model>) -> Self { + let model = value.1; + let writable = Entity::writable(&model, value.0); EducationFullInfo { info: EducationInfo { - id: value.id, - title: value.title, + id: model.id, + title: model.title, }, - content: value.content, - problem: value.problem_id.map(Into::into), + content: model.content, + problem: model.problem_id.map(Into::into), + writable, } } } + +impl<'a> WithAuthTrait for Model {} + impl From for EducationInfo { fn from(value: PartialModel) -> Self { EducationInfo { @@ -262,6 +268,6 @@ impl Education for ArcServer { .map_err(Into::::into)? .ok_or(Error::NotInDB)?; - Ok(Response::new(model.into())) + Ok(Response::new(model.with_auth(&auth).into())) } } diff --git a/backend/src/endpoint/problem.rs b/backend/src/endpoint/problem.rs index 91e2b44..575373f 100644 --- a/backend/src/endpoint/problem.rs +++ b/backend/src/endpoint/problem.rs @@ -3,6 +3,32 @@ use grpc::backend::problem_server::*; use crate::entity::{problem::Paginator, problem::*, *}; +impl<'a> From> for ProblemFullInfo { + fn from(value: WithAuth<'a, Model>) -> Self { + let model = value.1; + let writable = Entity::writable(&model, value.0); + ProblemFullInfo { + content: model.content.clone(), + tags: model.tags.clone(), + difficulty: model.difficulty, + public: model.public, + time: model.time as u64, + memory: model.memory as u64, + info: ProblemInfo { + id: model.id, + title: model.title, + submit_count: model.submit_count, + ac_rate: model.ac_rate, + difficulty: model.difficulty, + }, + author: model.user_id, + writable, + } + } +} + +impl<'a> WithAuthTrait for Model {} + impl From for ProblemInfo { fn from(value: PartialModel) -> Self { ProblemInfo { @@ -15,27 +41,6 @@ impl From for ProblemInfo { } } -impl From for ProblemFullInfo { - fn from(value: Model) -> Self { - ProblemFullInfo { - content: value.content.clone(), - tags: value.tags.clone(), - difficulty: value.difficulty, - public: value.public, - time: value.time as u64, - memory: value.memory as u64, - info: ProblemInfo { - id: value.id, - title: value.title, - submit_count: value.submit_count, - ac_rate: value.ac_rate, - difficulty: value.difficulty, - }, - author: value.user_id, - } - } -} - #[async_trait] impl Problem for ArcServer { #[instrument(skip_all, level = "debug")] @@ -99,7 +104,7 @@ impl Problem for ArcServer { .map_err(Into::::into)? .ok_or(Error::NotInDB)?; - Ok(Response::new(model.into())) + Ok(Response::new(model.with_auth(&auth).into())) } #[instrument(skip_all, level = "debug")] async fn create(&self, req: Request) -> Result, Status> { @@ -379,6 +384,6 @@ impl Problem for ArcServer { .map_err(Into::::into)? .ok_or(Error::NotInDB)?; - Ok(Response::new(model.into())) + Ok(Response::new(model.with_auth(&auth).into())) } } diff --git a/backend/src/endpoint/testcase.rs b/backend/src/endpoint/testcase.rs index db6dd7b..f70270e 100644 --- a/backend/src/endpoint/testcase.rs +++ b/backend/src/endpoint/testcase.rs @@ -4,17 +4,22 @@ use grpc::backend::testcase_server::*; use crate::entity::{testcase::*, *}; -impl From for TestcaseFullInfo { - fn from(value: Model) -> Self { +impl<'a> From> for TestcaseFullInfo { + fn from(value: WithAuth<'a, Model>) -> Self { + let model = value.1; + let writable = Entity::writable(&model, value.0); TestcaseFullInfo { - id: value.id, - score: value.score, - inputs: value.input, - outputs: value.output, + id: model.id, + score: model.score, + inputs: model.input, + outputs: model.output, + writable, } } } +impl<'a> WithAuthTrait for Model {} + impl From for TestcaseInfo { fn from(value: Model) -> Self { TestcaseInfo { @@ -232,6 +237,6 @@ impl Testcase for ArcServer { .map_err(Into::::into)? .ok_or(Error::NotInDB)?; - Ok(Response::new(model.into())) + Ok(Response::new(model.with_auth(&auth).into())) } } diff --git a/backend/src/endpoint/user.rs b/backend/src/endpoint/user.rs index 6759c76..a4c211f 100644 --- a/backend/src/endpoint/user.rs +++ b/backend/src/endpoint/user.rs @@ -7,6 +7,20 @@ use crate::{ entity::user::{Paginator, *}, }; +impl<'a> From> for UserFullInfo { + fn from(value: WithAuth<'a, Model>) -> Self { + let model = value.1; + let writable = Entity::writable(&model, value.0); + UserFullInfo { + hashed_pwd: model.password.clone(), + info: model.into(), + writable, + } + } +} + +impl<'a> WithAuthTrait for Model {} + impl From for UserInfo { fn from(value: Model) -> Self { UserInfo { @@ -261,7 +275,7 @@ impl User for ArcServer { } #[instrument(skip_all, level = "debug")] - async fn my_info(&self, req: Request<()>) -> Result, Status> { + async fn my_info(&self, req: Request<()>) -> Result, Status> { let (auth, _) = self .parse_request_n(req, NonZeroU32!(5)) .in_current_span() @@ -277,6 +291,6 @@ impl User for ArcServer { "token should be deleted before user can request its info after user deletion", ))?; - Ok(Response::new(model.into())) + Ok(Response::new(model.with_auth(&auth).into())) } } diff --git a/grpc/proto/backend.proto b/grpc/proto/backend.proto index 7bdd2ea..5b7590b 100644 --- a/grpc/proto/backend.proto +++ b/grpc/proto/backend.proto @@ -102,6 +102,7 @@ message ProblemFullInfo { required uint64 time = 6; required uint64 memory = 7; required int32 author = 9; + required bool writable = 10; } message ListProblemRequest { @@ -308,6 +309,7 @@ message EducationFullInfo { required EducationInfo info = 1; required string content = 2; optional int32 problem = 4; + required bool writable = 5; } message ListEducationResponse { @@ -383,6 +385,7 @@ message TestcaseFullInfo { required uint32 score = 2; required bytes inputs = 3; required bytes outputs = 4; + required bool writable = 5; } message ListTestcaseResponse { @@ -457,6 +460,7 @@ message ContestFullInfo { required ContestInfo info = 1; required string content = 2; required int32 host = 3; + required bool writable = 5; } message ListContestResponse { @@ -558,6 +562,7 @@ message UserInfo { message UserFullInfo { required UserInfo info = 1; required bytes hashed_pwd = 2; + required bool writable = 3; } message ListUserResponse { @@ -626,7 +631,7 @@ service User { rpc UpdatePassword(UpdatePasswordRequest) returns (google.protobuf.Empty); - rpc MyInfo(google.protobuf.Empty) returns (UserInfo); + rpc MyInfo(google.protobuf.Empty) returns (UserFullInfo); } message TokenInfo { @@ -658,6 +663,7 @@ message ChatInfo { required int32 problem_id = 2; required google.protobuf.Timestamp create_at = 3; required string message = 4; + required bool writable = 6; } message ListChatResponse {