Skip to content

Commit c49d6bf

Browse files
committed
feat: add authenticated user
1 parent d0640f2 commit c49d6bf

File tree

6 files changed

+55
-35
lines changed

6 files changed

+55
-35
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ version = "0.1.0"
44
edition = "2021"
55

66
[dependencies]
7+
bcrypt = "0.15.1"
78
chrono = { version = "0.4.38", features = ["serde"] }
89
diesel = { version = "2.2.4", features = ["postgres", "numeric", "serde_json", "chrono"] }
910
dotenv = "0.15.0"
11+
jsonwebtoken = "9.3.0"
1012
rocket = { version = "0.5.1", features = ["json"] }
1113
rust_decimal = { version = "1.36.0", features = ["db-diesel-postgres", "serde-with-str"] }
1214
serde = "1.0.213"

src/auth/mod.rs

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1 @@
1-
pub struct ApiKey(pub String);
2-
use rocket::{http::Status, request::{FromRequest, Outcome}, Request};
3-
use std::env;
4-
5-
#[derive(Debug)]
6-
pub enum ApiKeyError {
7-
Missing,
8-
Invalid,
9-
}
10-
11-
#[rocket::async_trait]
12-
impl<'r> FromRequest<'r> for ApiKey {
13-
type Error = ApiKeyError;
14-
15-
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
16-
let api_key = request.headers().get_one("x-api-key");
17-
18-
match api_key {
19-
None => Outcome::Error((Status::Unauthorized, ApiKeyError::Missing)),
20-
Some(api_key) => {
21-
if api_key == env::var("AUTH_API_KEY").unwrap() {
22-
Outcome::Success(ApiKey(api_key.to_string()))
23-
} else {
24-
Outcome::Error((Status::Unauthorized, ApiKeyError::Invalid))
25-
}
26-
}
27-
}
28-
}
29-
}
1+
pub mod user;

src/auth/user.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use crate::models::user::User;
2+
use diesel::prelude::*;
3+
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
4+
use rocket::http::Status;
5+
use rocket::request::{FromRequest, Outcome};
6+
use rocket::Request;
7+
use serde::{Deserialize, Serialize};
8+
9+
pub struct AuthenticatedUser(pub User);
10+
11+
#[derive(Debug, Serialize, Deserialize)]
12+
pub(crate) struct Claims {
13+
pub(crate) sub: i32,
14+
pub(crate) role: String,
15+
pub(crate) exp: usize,
16+
}
17+
18+
#[rocket::async_trait]
19+
impl<'r> FromRequest<'r> for AuthenticatedUser {
20+
type Error = ();
21+
22+
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
23+
let token = request.headers().get_one("Authorization");
24+
25+
match token {
26+
Some(token) if token.starts_with("Bearer ") => {
27+
let token = &token[7..];
28+
let decoding_key = DecodingKey::from_secret("your_secret_key".as_ref());
29+
match decode::<Claims>(token, &decoding_key, &Validation::new(Algorithm::HS256)) {
30+
Ok(token_data) => {
31+
let mut conn = crate::db::establish_connection();
32+
match crate::schema::users::dsl::users
33+
.find(token_data.claims.sub)
34+
.first::<User>(&mut conn)
35+
{
36+
Ok(user) => Outcome::Success(AuthenticatedUser(user)),
37+
Err(_) => Outcome::Error((Status::Unauthorized, ())),
38+
}
39+
}
40+
Err(_) => Outcome::Error((Status::Unauthorized, ())),
41+
}
42+
}
43+
_ => Outcome::Error((Status::Unauthorized, ())),
44+
}
45+
}
46+
}

src/models/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
pub mod snack;
2-
mod user;
2+
pub(crate) mod user;

src/routes/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
pub mod snack;
1+
pub mod snack;
2+
pub mod auth;

src/routes/snack.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ pub struct UpdateSnack {
1717
}
1818

1919
#[post("/snack", data = "<snack_data>")]
20-
pub fn create_snack(_api_key: ApiKey, snack_data: Json<NewSnack>) -> Result<Json<Snack>, Status> {
20+
pub fn create_snack(snack_data: Json<NewSnack>) -> Result<Json<Snack>, Status> {
2121
let snack = snack_data.into_inner();
2222

2323
let mut conn = db::establish_connection();
@@ -33,7 +33,7 @@ pub fn create_snack(_api_key: ApiKey, snack_data: Json<NewSnack>) -> Result<Json
3333
}
3434

3535
#[get("/snacks")]
36-
pub fn list_snacks(_api_key: ApiKey) -> Result<Json<Vec<Snack>>, Status> {
36+
pub fn list_snacks() -> Result<Json<Vec<Snack>>, Status> {
3737
let mut conn = db::establish_connection();
3838

3939
snacks
@@ -49,7 +49,6 @@ pub fn list_snacks(_api_key: ApiKey) -> Result<Json<Vec<Snack>>, Status> {
4949

5050
#[patch("/snack/<snack_id>", data = "<snack_data>")]
5151
pub fn update_snack(
52-
_api_key: ApiKey,
5352
snack_id: i32,
5453
snack_data: Json<UpdateSnack>,
5554
) -> Result<Json<Snack>, Status> {
@@ -68,7 +67,7 @@ pub fn update_snack(
6867
})
6968
}
7069
#[delete("/snack/<snack_id>")]
71-
pub fn delete_snack(_api_key: ApiKey, snack_id: i32) -> Status {
70+
pub fn delete_snack(snack_id: i32) -> Status {
7271
let mut conn = db::establish_connection();
7372

7473
match diesel::delete(snacks.find(snack_id)).execute(&mut conn) {

0 commit comments

Comments
 (0)