Skip to content

Commit

Permalink
feat: 🚧 draft userset endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
Eason0729 committed Nov 23, 2023
1 parent d1c4f2a commit 4a4b121
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 14 deletions.
2 changes: 2 additions & 0 deletions backend/entity/src/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub struct Model {
#[sea_orm(primary_key, auto_increment = true)]
pub id: i32,
pub permission: u64,
#[sea_orm(indexed)]
pub score: u32,
pub username: String,
pub password: Vec<u8>,
#[sea_orm(column_type = "Timestamp", on_insert = "current_timestamp")]
Expand Down
3 changes: 2 additions & 1 deletion backend/src/controller/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ macro_rules! set_bit_value {
};
}

#[derive(Debug, Clone, Copy, Default)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct UserPermBytes(pub u64);

impl UserPermBytes {
Expand All @@ -223,3 +223,4 @@ set_bit_value!(UserPermBytes, manage_submit, 4);
set_bit_value!(UserPermBytes, publish, 5);
set_bit_value!(UserPermBytes, link, 6);
set_bit_value!(UserPermBytes, manage_contest, 7);
set_bit_value!(UserPermBytes, manage_user, 8);
30 changes: 28 additions & 2 deletions backend/src/endpoint/testcase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,40 @@ impl From<Model> for TestcaseInfo {

#[async_trait]
impl TestcaseSet for Arc<Server> {
async fn list(
&self,
req: Request<ListRequest>,
) -> Result<Response<ListTestcaseResponse>, Status> {
let (auth, req) = self.parse_request(req).await?;

let mut reverse = false;
let mut pager: Pager<Entity> = match req.request.ok_or(Error::NotInPayload("request"))? {
list_request::Request::Create(create) => {
Pager::sort_search(create.sort_by(), create.reverse)
}
list_request::Request::Pager(old) => {
reverse = old.reverse;
<Pager<Entity> as HasParentPager<problem::Entity, Entity>>::from_raw(old.session)?
}
};

let list = pager
.fetch(req.size, req.offset.unwrap_or_default(), reverse, &auth)
.await?
.into_iter()
.map(|x| x.into())
.collect();

let next_session = pager.into_raw();

Ok(Response::new(ListTestcaseResponse { list, next_session }))
}
async fn create(
&self,
req: Request<CreateTestcaseRequest>,
) -> Result<Response<TestcaseId>, Status> {
let db = DB.get().unwrap();
let (auth, req) = self.parse_request(req).await?;

let (user_id, perm) = auth.ok_or_default()?;

let uuid = Uuid::parse_str(&req.request_id).map_err(Error::InvaildUUID)?;
Expand All @@ -101,7 +128,6 @@ impl TestcaseSet for Arc<Server> {
async fn update(&self, req: Request<UpdateTestcaseRequest>) -> Result<Response<()>, Status> {
let db = DB.get().unwrap();
let (auth, req) = self.parse_request(req).await?;

let (user_id, perm) = auth.ok_or_default()?;

let uuid = Uuid::parse_str(&req.request_id).map_err(Error::InvaildUUID)?;
Expand Down
215 changes: 209 additions & 6 deletions backend/src/endpoint/user.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
use super::endpoints::*;
use super::tools::*;

use crate::controller::token::UserPermBytes;
use crate::endpoint::util::hash;
use crate::grpc::backend::user_set_server::*;
use crate::grpc::backend::*;

use entity::{user::*, *};

impl Filter for Entity {
fn read_filter<S: QueryFilter + Send>(query: S, _: &Auth) -> Result<S, Error> {
Ok(query)
}

fn write_filter<S: QueryFilter + Send>(query: S, auth: &Auth) -> Result<S, Error> {
let (user_id,perm)=auth.ok_or_default()?;
if perm.can_root() || perm.can_manage_user() {
return Ok(query);
}
Ok(query.filter(Column::Id.eq(user_id)))
}
}

impl From<i32> for UserId {
fn from(value: i32) -> Self {
Self { id: value }
Expand All @@ -18,27 +34,214 @@ impl From<UserId> for i32 {
}
}

impl From<Model> for UserInfo {
fn from(value: Model) -> Self {
UserInfo {
username: value.username,
score: value.score,
id: value.id.into(),
}
}
}

impl From<UserPermBytes> for Permission {
fn from(value: UserPermBytes) -> Self {
Permission {
can_manage_contest: value.can_manage_contest(),
can_manage_problem: value.can_manage_problem(),
can_manage_announcement: value.can_manage_announcement(),
can_manage_education: value.can_manage_education(),
can_manage_user: value.can_manage_user(),
can_root: value.can_root(),
can_link: value.can_link(),
can_publish: value.can_publish(),
}
}
}

impl From<Permission> for UserPermBytes {
fn from(value: Permission) -> Self {
let mut perm = UserPermBytes::default();

if value.can_manage_contest {
perm.grant_manage_contest(true);
}
if value.can_manage_problem {
perm.grant_manage_problem(true);
}
if value.can_manage_announcement {
perm.grant_manage_announcement(true);
}
if value.can_manage_education {
perm.grant_manage_education(true);
}
if value.can_manage_user {
perm.grant_manage_user(true);
}
if value.can_root {
perm.grant_root(true);
}
if value.can_link {
perm.grant_link(true);
}
if value.can_publish {
perm.grant_publish(true);
}

perm
}
}

#[async_trait]
impl UserSet for Arc<Server> {
async fn list(&self, req: Request<ListRequest>) -> Result<Response<ListUserResponse>, Status> {
Err(Status::unimplemented("unimplemented"))
let (auth, req) = self.parse_request(req).await?;

let mut reverse = false;
let mut pager: Pager<Entity> = match req.request.ok_or(Error::NotInPayload("request"))? {
list_request::Request::Create(create) => {
Pager::sort_search(create.sort_by(), create.reverse)
}
list_request::Request::Pager(old) => {
reverse = old.reverse;
<Pager<Entity> as NoParentPager<Entity>>::from_raw(old.session)?
}
};

let list = pager
.fetch(req.size, req.offset.unwrap_or_default(), reverse, &auth)
.await?
.into_iter()
.map(|x| x.into())
.collect();

let next_session = pager.into_raw();

Ok(Response::new(ListUserResponse { list, next_session }))
}
async fn search_by_text(
&self,
req: Request<TextSearchRequest>,
) -> Result<Response<ListUserResponse>, Status> {
Err(Status::unimplemented("unimplemented"))
let (auth, req) = self.parse_request(req).await?;

let mut reverse = false;
let mut pager: Pager<Entity> = match req.request.ok_or(Error::NotInPayload("request"))? {
text_search_request::Request::Text(create) => Pager::text_search(create),
text_search_request::Request::Pager(old) => {
reverse = old.reverse;
<Pager<_> as NoParentPager<Entity>>::from_raw(old.session)?
}
};

let list = pager
.fetch(req.size, req.offset.unwrap_or_default(), reverse, &auth)
.await?
.into_iter()
.map(|x| x.into())
.collect();

let next_session = pager.into_raw();

Ok(Response::new(ListUserResponse { list, next_session }))
}
async fn full_info(&self, req: Request<UserId>) -> Result<Response<UserFullInfo>, Status> {
Err(Status::unimplemented("unimplemented"))
let (auth, _) = self.parse_request(req).await?;

if !auth.is_root() {
return Err(Error::Unauthenticated.into());
}

Err(Status::cancelled("deprecated"))
}
async fn create(&self, req: Request<CreateUserRequest>) -> Result<Response<()>, Status> {
Err(Status::unimplemented("unimplemented"))
async fn create(&self, req: Request<CreateUserRequest>) -> Result<Response<UserId>, Status> {
let db = DB.get().unwrap();
let (auth, req) = self.parse_request(req).await?;
let (user_id, perm) = auth.ok_or_default()?;

let uuid = Uuid::parse_str(&req.request_id).map_err(Error::InvaildUUID)?;
if let Some(x) = self.dup.check(user_id, &uuid) {
return Ok(Response::new(x.into()));
};

if !(perm.can_root() || perm.can_manage_user()) {
return Err(Error::PremissionDeny("Can't create user").into());
}

let mut model: ActiveModel = Default::default();

fill_active_model!(model, req.info, username);

model.password = ActiveValue::set(hash::hash(req.info.password.as_str()));
let new_perm = Into::<UserPermBytes>::into(req.info.permission);

if new_perm != UserPermBytes::default() && !perm.can_root() {
return Err(Error::PremissionDeny("Can't set permission").into());
}
model.permission = ActiveValue::set(new_perm.0);

let model = model.save(db).await.map_err(Into::<Error>::into)?;

self.dup.store(user_id, uuid, model.id.clone().unwrap());

Ok(Response::new(model.id.unwrap().into()))
}
async fn update(&self, req: Request<UpdateUserRequest>) -> Result<Response<()>, Status> {
Err(Status::unimplemented("unimplemented"))
let db = DB.get().unwrap();
let (auth, req) = self.parse_request(req).await?;

let (user_id, perm) = auth.ok_or_default()?;

let uuid = Uuid::parse_str(&req.request_id).map_err(Error::InvaildUUID)?;
if self.dup.check(user_id, &uuid).is_some() {
return Ok(Response::new(()));
};

if !(perm.can_root() || perm.can_manage_problem()) {
return Err(Error::PremissionDeny("Can't update user").into());
}

let mut model = Entity::write_filter(Entity::find_by_id(req.id), &auth)?
.one(db)
.await
.map_err(Into::<Error>::into)?
.ok_or(Error::NotInDB("user"))?
.into_active_model();

if let Some(username) = req.info.username {
model.username = ActiveValue::set(username);
}
if let Some(password) = req.info.password {
model.password = ActiveValue::set(hash::hash(password.as_str()));
}
if let Some(permission) = req.info.permission {
if !perm.can_root() {
return Err(Error::PremissionDeny("Can't set permission").into());
}
model.permission = ActiveValue::set(Into::<UserPermBytes>::into(permission).0);
}

let model = model.update(db).await.map_err(Into::<Error>::into)?;

self.dup.store(user_id, uuid, model.id);

Ok(Response::new(()))
}
async fn remove(&self, req: Request<UserId>) -> Result<Response<()>, Status> {
let db = DB.get().unwrap();
let (auth, req) = self.parse_request(req).await?;

Entity::write_filter(Entity::delete_by_id(Into::<i32>::into(req.id)), &auth)?
.exec(db)
.await
.map_err(Into::<Error>::into)?;

Ok(Response::new(()))
}
async fn update_password(
&self,
req: Request<UpdatePasswordRequest>,
) -> Result<Response<()>, Status> {
Err(Status::unimplemented("unimplemented"))
}
}
28 changes: 27 additions & 1 deletion backend/src/endpoint/util/pagination.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,32 @@ impl PagerTrait for contest::Entity {
model.id
}
async fn query_filter(select: Select<Self>, auth: &Auth) -> Result<Select<Self>, Error> {
todo!()
contest::Entity::read_filter(select, auth)
}
}

#[tonic::async_trait]
impl PagerTrait for user::Entity {
const TYPE_NUMBER: i32 = 1929833;

const COL_ID: Self::Column = user::Column::Id;

const COL_TEXT: &'static [Self::Column] = &[user::Column::Username];

const COL_SELECT: &'static [Self::Column] = &[
user::Column::Id,
user::Column::Username,
user::Column::Permission,
user::Column::CreateAt,
];

type ParentMarker = NoParent;

fn get_id(model: &Self::Model) -> i32 {
model.id
}

async fn query_filter(select: Select<Self>, auth: &Auth) -> Result<Select<Self>, Error> {
user::Entity::read_filter(select, auth)
}
}
Loading

0 comments on commit 4a4b121

Please sign in to comment.