Skip to content

Commit

Permalink
kek
Browse files Browse the repository at this point in the history
  • Loading branch information
Hy-u-3a4eM committed Feb 6, 2024
1 parent 9994f0e commit bb9e1d7
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 45 deletions.
14 changes: 14 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432
POSTGRES_USER=postgres
POSTGRES_PASSWORD=aboba
POSTGRES_DB=rinab

DATABASE_URL=postgresql://postgres:[email protected]:5432/rinab?schema=public

PGADMIN_DEFAULT_EMAIL=[email protected]
PGADMIN_DEFAULT_PASSWORD=aboba

JWT_SECRET=agjGKfVxVf2TbeuzLS8wKVnQchBEfz5XRWtSmDUkcmgErReWDPAa689VSKVTrrM2
JWT_EXPIRED_IN=60m
JWT_MAXAGE=60
86 changes: 86 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ once_cell = "1.19.0"
serde_json = "1.0.112"
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing = "0.1.40"
axum-extra = { version = "0.9.2", features = ["typed-header"] }
axum-extra = { version = "0.9.2", features = ["typed-header", "cookie"] }
jsonwebtoken = "9.2.0"
tower-http = { version = "0.5.1", features = ["cors"] }
chrono = { version = "0.4.33", features = ["serde"] }
uuid = { version = "1.7.0", features = ["v4", "serde"] }
99 changes: 99 additions & 0 deletions src/authorization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
use std::sync::Arc;

use axum::{
extract::State,
http::{header, Request, StatusCode},
middleware::Next,
response::IntoResponse,
Json,
};

use axum_extra::extract::cookie::CookieJar;
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::Serialize;

use crate::{
model::{TokenClaims, User},
AppState,
};

#[derive(Debug, Serialize)]
pub struct ErrorResponse {
pub status: &'static str,
pub message: String,
}

pub async fn auth<B>(
cookie_jar: CookieJar,
State(data): State<Arc<AppState>>,
mut req: Request<B>,
next: Next<B>,
) -> Result<impl IntoResponse, (StatusCode, Json<ErrorResponse>)> {
let token = cookie_jar
.get("token")
.map(|cookie| cookie.value().to_string())
.or_else(|| {
req.headers()
.get(header::AUTHORIZATION)
.and_then(|auth_header| auth_header.to_str().ok())
.and_then(|auth_value| {
if auth_value.starts_with("Bearer ") {
Some(auth_value[7..].to_owned())
} else {
None
}
})
});

let token = token.ok_or_else(|| {
let json_error = ErrorResponse {
status: "fail",
message: "You are not logged in, please provide token".to_string(),
};
(StatusCode::UNAUTHORIZED, Json(json_error))
})?;

let claims = decode::<TokenClaims>(
&token,
&DecodingKey::from_secret(data.env.jwt_secret.as_ref()),
&Validation::default(),
)
.map_err(|_| {
let json_error = ErrorResponse {
status: "fail",
message: "Invalid token".to_string(),
};
(StatusCode::UNAUTHORIZED, Json(json_error))
})?
.claims;

let user_id = uuid::Uuid::parse_str(&claims.sub).map_err(|_| {
let json_error = ErrorResponse {
status: "fail",
message: "Invalid token".to_string(),
};
(StatusCode::UNAUTHORIZED, Json(json_error))
})?;

let user = sqlx::query_as!(User, "SELECT * FROM users WHERE id = $1", user_id)
.fetch_optional(&data.db_pool)
.await
.map_err(|e| {
let json_error = ErrorResponse {
status: "fail",
message: format!("Error fetching user from database: {}", e),
};
(StatusCode::INTERNAL_SERVER_ERROR, Json(json_error))
})?;

let user = user.ok_or_else(|| {
let json_error = ErrorResponse {
status: "fail",
message: "The user belonging to this token no longer exists".to_string(),
};
(StatusCode::UNAUTHORIZED, Json(json_error))
})?;

req.extensions_mut().insert(user);
Ok(next.run(req).await)
}
22 changes: 22 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#[derive(Debug, Clone)]
pub struct Config {
pub database_url: String,
pub jwt_secret: String,
pub jwt_expires_in: String,
pub jwt_maxage: i32,
}

impl Config {
pub fn init() -> Config {
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let jwt_secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");
let jwt_expires_in = std::env::var("JWT_EXPIRED_IN").expect("JWT_EXPIRED_IN must be set");
let jwt_maxage = std::env::var("JWT_MAXAGE").expect("JWT_MAXAGE must be set");
Config {
database_url,
jwt_secret,
jwt_expires_in,
jwt_maxage: jwt_maxage.parse::<i32>().unwrap(),
}
}
}
35 changes: 26 additions & 9 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use std::sync::Arc;

use axum::{
extract::State, Json
};
use axum::{Extension, extract::State, Json};
use axum::response::IntoResponse;
use jsonwebtoken::{encode, Header};
use crate::{AppState, AuthBody, AuthError, Authlogin, Claims, User, KEYS};
use crate::model::User;
use crate::response::FilteredUser;

pub async fn login(State(data): State<Arc<AppState>>, Json(login): Json<Authlogin>) -> Result<Json<AuthBody>, AuthError> {
pub async fn login(State(data): State<Arc<AppState>>, Json(login): Json<Authlogin>) -> Result<impl IntoResponse, AuthError> {
// Получаем пользователя по имени из базы данных
let user = sqlx::query_as::<_, User>(
"SELECT id, username, password FROM users WHERE username = $1",
Expand Down Expand Up @@ -37,9 +38,25 @@ pub async fn login(State(data): State<Arc<AppState>>, Json(login): Json<Authlogi
Ok(Json(AuthBody::new(token)))
}

pub async fn get_user(State(data): State<Arc<AppState>>, claims: Claims) -> Result<String, AuthError> {
// Send the user data to the user
Ok(format!(
"Welcome to the user area :)\nYour data:\n{claims}",
))
pub async fn get_me_handler(
Extension(user): Extension<User>,
) -> Result<impl IntoResponse, (StatusCode, Json<serde_json::Value>)> {
let json_response = serde_json::json!({
"status": "success",
"data": serde_json::json!({
"user": filter_user_record(&user)
})
});

Ok(Json(json_response))
}

fn filter_user_record(user: &User) -> FilteredUser {
FilteredUser {
id: user.id.to_string(),
username: user.username.to_owned(),
name: user.name.to_owned(),
createdAt: user.created_at.unwrap(),
updatedAt: user.updated_at.unwrap(),
}
}
Loading

0 comments on commit bb9e1d7

Please sign in to comment.