Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 3c4be71

Browse files
committed
fix: improve the RSA encryption so that it can parse hex strings
1 parent d840274 commit 3c4be71

File tree

11 files changed

+197
-27
lines changed

11 files changed

+197
-27
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ socketioxide = { version = "0.12.0", features = ["state", "extensions", "tracing
3232
tokio = { version = "1.37.0", features = ["full"] }
3333
tower = "0.4.13"
3434
tower-http = "0.5.2"
35+
tracing = { version = "0.1.40", features = ["log"] }
3536
tracing-subscriber = { version = "0.3.18", features = ["tracing", "time", "serde", "serde_json", "json", "regex"] }
3637
uuid = "1.8.0"
3738
zerocopy = "0.7.32"

README.md

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
# ZVMS 4 Backend Implementation with Rust
2-
3-
The first version of backend of ZVMS 4 is a `shit mountain` of code.
4-
5-
Since I am learning `Rust`, I decided to rewrite the backend in `Rust`.
6-
7-
Technologies used:
8-
9-
- `axum`: Web framework
10-
- `tokio`: Async runtime
11-
- `mongodb`: Database
12-
13-
> You should have `Rust` installed in your system to run this project.
14-
15-
> You need to create `src/config.rs` with following code:
16-
17-
```rust
18-
pub const MONGO_URL: &str = "<MONGO_URL>";
19-
```
1+
# ZVMS 4 Backend Implementation with Rust
2+
3+
[![JWT](https://jwt.io/img/badge-compatible.svg)](https://jwt.io/)
4+
5+
![Test](https://github.com/zvms/zvms4-backend-rust/actions/workflows/test.yml/badge.svg) ![Build](https://github.com/zvms/zvms4-backend-rust/actions/workflows/release.yml/badge.svg)
6+
7+
The first version of backend of ZVMS 4 is a `shit mountain` of code.
8+
9+
Since I am learning `Rust`, I decided to rewrite the backend in `Rust`.
10+
11+
Technologies used:
12+
13+
- `axum`: Web framework
14+
- `tokio`: Async runtime
15+
- `mongodb`: Database

aes256.key

Lines changed: 0 additions & 1 deletion
This file was deleted.

src/main.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ fn on_connect(socket: SocketRef, Data(data): Data<Value>) {
3838

3939
#[tokio::main]
4040
async fn main() {
41-
tracing_subscriber::fmt::init();
41+
tracing_subscriber::fmt().init();
4242

4343
let client = database::create_client()
4444
.await
@@ -64,6 +64,10 @@ async fn main() {
6464
post(routers::activities::insert::insert_activity),
6565
)
6666
.route("/activity/:id", get(routers::activities::read::read_one))
67+
.route(
68+
"/activity/:id",
69+
delete(routers::activities::remove::remove_activity),
70+
)
6771
.route(
6872
"/activity/:id/name",
6973
put(routers::activities::update::update_activity_name),
@@ -73,8 +77,8 @@ async fn main() {
7377
put(routers::activities::update::update_activity_description),
7478
)
7579
.route(
76-
"/activity/:id",
77-
delete(routers::activities::remove::remove_activity),
80+
"/activity/:id/member",
81+
post(routers::activities::members::insert::insert_member_into_activity),
7882
)
7983
.route("/user/auth", post(routers::auth::login))
8084
.layer(Extension(shared_client.clone()));
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
use crate::{
2+
models::{
3+
activities::{Activity, ActivityMember},
4+
groups::GroupPermission,
5+
response::{ErrorResponse, ResponseStatus, SuccessResponse},
6+
},
7+
utils::jwt::UserData,
8+
};
9+
use axum::{
10+
extract::{Extension, Json, Path},
11+
http::StatusCode,
12+
response::IntoResponse,
13+
};
14+
use bson::{doc, oid::ObjectId};
15+
use mongodb::Database;
16+
use std::{str::FromStr, sync::Arc};
17+
use tokio::sync::Mutex;
18+
19+
pub async fn insert_member_into_activity(
20+
Extension(db): Extension<Arc<Mutex<Database>>>,
21+
user: UserData,
22+
Path(id): Path<String>,
23+
Json(activity_member): Json<ActivityMember>,
24+
) -> impl IntoResponse {
25+
let db = db.lock().await;
26+
let collection = db.collection("activities");
27+
let activity_id = ObjectId::from_str(&id).unwrap();
28+
let activity = collection.find_one(doc! {"_id": activity_id}, None).await;
29+
if let Err(_) = activity {
30+
let response = ErrorResponse {
31+
status: ResponseStatus::Error,
32+
code: 404,
33+
message: "Activity not found".to_string(),
34+
};
35+
let response = serde_json::to_string(&response).unwrap();
36+
return (StatusCode::NOT_FOUND, Json(response));
37+
}
38+
let activity = activity.unwrap();
39+
if let None = activity {
40+
let response = ErrorResponse {
41+
status: ResponseStatus::Error,
42+
code: 404,
43+
message: "Activity not found".to_string(),
44+
};
45+
let response = serde_json::to_string(&response).unwrap();
46+
return (StatusCode::NOT_FOUND, Json(response));
47+
}
48+
let activity: Activity = bson::from_document(activity.unwrap()).unwrap();
49+
let members = activity.members.unwrap_or_default();
50+
// Check if the activity contains the member
51+
if members
52+
.iter()
53+
.any(|member| member._id == ObjectId::from_str(&activity_member._id.to_hex()).unwrap())
54+
{
55+
let response = ErrorResponse {
56+
status: ResponseStatus::Error,
57+
code: 400,
58+
message: "Member already exists".to_string(),
59+
};
60+
let response = serde_json::to_string(&response).unwrap();
61+
return (StatusCode::BAD_REQUEST, Json(response));
62+
}
63+
if user.perms.contains(&GroupPermission::Admin) || user.perms.contains(&GroupPermission::Department) {} else {
64+
let response = ErrorResponse {
65+
status: ResponseStatus::Error,
66+
code: 403,
67+
message: "Permission denied".to_string(),
68+
};
69+
let response = serde_json::to_string(&response).unwrap();
70+
return (StatusCode::FORBIDDEN, Json(response));
71+
}
72+
let member = bson::to_document(&activity_member);
73+
if let Err(_) = member {
74+
let response = ErrorResponse {
75+
status: ResponseStatus::Error,
76+
code: 400,
77+
message: "Invalid member".to_string(),
78+
};
79+
let response = serde_json::to_string(&response).unwrap();
80+
return (StatusCode::BAD_REQUEST, Json(response));
81+
}
82+
let result = collection
83+
.update_one(
84+
doc! {"_id": activity_id},
85+
doc! {
86+
"$push": {
87+
"members": member.unwrap()
88+
}
89+
},
90+
None,
91+
)
92+
.await;
93+
if let Ok(_) = result {
94+
let response: SuccessResponse<_, ()> = SuccessResponse {
95+
status: ResponseStatus::Success,
96+
code: 200,
97+
data: (),
98+
metadata: None,
99+
};
100+
let response = serde_json::to_string(&response).unwrap();
101+
(StatusCode::OK, Json(response))
102+
} else {
103+
let response = ErrorResponse {
104+
status: ResponseStatus::Error,
105+
code: 500,
106+
message: "Failed to insert member".to_string(),
107+
};
108+
let response = serde_json::to_string(&response).unwrap();
109+
(StatusCode::INTERNAL_SERVER_ERROR, Json(response))
110+
}
111+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod insert;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
use crate::{models::{activities::Activity}};

src/routers/auth/mod.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub struct LoginCredentials {
3737
pub async fn login(
3838
Extension(client): Extension<Arc<Mutex<Database>>>,
3939
Json(body): Json<LoginRequest>,
40-
4140
) -> impl IntoResponse {
4241
let client = client.lock().await;
4342
let collection = client.collection("users");
@@ -67,7 +66,18 @@ pub async fn login(
6766
let user: Option<User> = user.unwrap();
6867
if let Some(user) = user {
6968
let keypair = load_keypair().await;
70-
let credentials = decrypt(&keypair.0, &body.credentials.as_bytes()).await;
69+
let credentials = hex::decode(&body.credentials);
70+
if let Err(_) = credentials {
71+
let response = ErrorResponse {
72+
status: ResponseStatus::Error,
73+
code: 400,
74+
message: "Invalid credentials".to_string(),
75+
};
76+
let response = json!(response);
77+
return (StatusCode::BAD_REQUEST, Json(response));
78+
}
79+
let credentials = credentials.unwrap();
80+
let credentials = decrypt(&keypair.0, &credentials).await;
7181
let credentials = serde_json::from_str(&credentials);
7282
if let Err(_) = credentials {
7383
let response = ErrorResponse {
@@ -92,6 +102,7 @@ pub async fn login(
92102
return (StatusCode::INTERNAL_SERVER_ERROR, Json(response));
93103
}
94104
let token = token.unwrap();
105+
println!("{:?}", token.clone());
95106
let response: SuccessResponse<String, ()> = SuccessResponse {
96107
status: ResponseStatus::Success,
97108
code: 200,

src/tests/apis/auth.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Use user: 105
3+
password: 105
4+
ObjectId: 65e6fa210edc81d012ec483a
5+
*/
6+
7+
#[cfg(test)]
8+
mod tests {
9+
use crate::database::create_client;
10+
use crate::routers::auth::{login, LoginCredentials, LoginRequest};
11+
use crate::utils::jwt::TokenType;
12+
use crate::utils::rsa::{encrypt, load_keypair};
13+
use axum::extract::Extension;
14+
use axum::response::IntoResponse;
15+
use axum::Json;
16+
use std::sync::Arc;
17+
use std::time::SystemTime;
18+
use tokio::sync::Mutex;
19+
20+
#[tokio::test]
21+
async fn test_login() {
22+
let client = create_client().await.unwrap();
23+
let client = Arc::new(Mutex::new(client));
24+
let client = Extension(client);
25+
let credential = LoginCredentials {
26+
password: "105".to_string(),
27+
timestamp: SystemTime::now()
28+
.duration_since(SystemTime::UNIX_EPOCH)
29+
.unwrap()
30+
.as_millis() as u64,
31+
};
32+
let credential = serde_json::to_string(&credential).unwrap();
33+
let (_, public_key) = load_keypair().await;
34+
let credential = encrypt(&public_key, credential.as_str());
35+
let credential = hex::encode(credential);
36+
let request = LoginRequest {
37+
credentials: credential,
38+
userid: "65e6fa210edc81d012ec483a".to_string(),
39+
term: TokenType::LongTerm
40+
};
41+
let result = login(client, Json(request)).await;
42+
let result = result.into_response();
43+
assert!(result.status().is_success())
44+
}
45+
}

0 commit comments

Comments
 (0)