diff --git a/api/src/guard/candidate_jwt.rs b/api/src/guard/candidate_jwt.rs new file mode 100644 index 0000000..2cba64b --- /dev/null +++ b/api/src/guard/candidate_jwt.rs @@ -0,0 +1,39 @@ +use config::DbConn; +use rocket::http::Status; +use rocket::outcome::Outcome; +use rocket::request::{self, FromRequest, Request}; +use rocket::response::status; +use rocket_contrib::json::Json; + +use portfolio_core::token::decode_candidate_token; + +impl<'a, 'r> FromRequest<'a, 'r> for UserToken { + type Error = status::Custom>; + fn from_request( + request: &'a Request<'r>, + ) -> request::Outcome>> { + let conn = request.guard::().unwrap(); + if let Some(authen_header) = request.headers().get_one("Authorization") { + let authen_str = authen_header.to_string(); + if authen_str.starts_with("Bearer") { + let token = authen_str[6..authen_str.len()].trim(); + if let Ok(token_data) = decode_candidate_token(token.to_string()) { + // if verify_token(&token_data, &conn) { + return Outcome::Success(token_data.claims); + // } + } + } + } + + Outcome::Failure(( + Status::BadRequest, + status::Custom( + Status::Unauthorized, + Json(Response { + message: String::from("Invalid token"), + data: serde_json::to_value("").unwrap(), + }), + ), + )) + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 4a05e7e..ad9a9b0 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -4,11 +4,13 @@ version = "0.1.0" edition = "2021" [dependencies] +serde = { version = "1.0", features = ["derive"] } portfolio-entity = { path = "../entity" } rand = "0.8.5" argon2 = "0.4.1" chrono = "0.4.22" jsonwebtoken = "8.1.1" +dotenv = "0.15.0" [dependencies.sea-orm] version = "^0.10.0" diff --git a/core/src/jwt.rs b/core/src/jwt.rs deleted file mode 100644 index 2b6675e..0000000 --- a/core/src/jwt.rs +++ /dev/null @@ -1,77 +0,0 @@ -use chrono::Utc; -use config::DbConn; -use jsonwebtoken::errors::Result; -use jsonwebtoken::TokenData; -use jsonwebtoken::{Header, Validation}; -use jsonwebtoken::{EncodingKey, DecodingKey}; -use models::response::Response; -use models::user::{ User, LoginInfoDTO }; -use rocket::http::Status; -use rocket::outcome::Outcome; -use rocket::request::{self, FromRequest, Request}; -use rocket::response::status; -use rocket_contrib::json::Json; - -static ONE_WEEK: i64 = 60 * 60 * 24 * 7; // in seconds - -#[derive(Debug, Serialize, Deserialize)] -pub struct UserToken { - // issued at - pub iat: i64, - // expiration - pub exp: i64, - // data - pub user: String, - pub login_session: String, -} - -impl<'a, 'r> FromRequest<'a, 'r> for UserToken { - type Error = status::Custom>; - fn from_request( - request: &'a Request<'r>, - ) -> request::Outcome>> { - let conn = request.guard::().unwrap(); - if let Some(authen_header) = request.headers().get_one("Authorization") { - let authen_str = authen_header.to_string(); - if authen_str.starts_with("Bearer") { - let token = authen_str[6..authen_str.len()].trim(); - if let Ok(token_data) = decode_token(token.to_string()) { - if verify_token(&token_data, &conn) { - return Outcome::Success(token_data.claims); - } - } - } - } - - Outcome::Failure(( - Status::BadRequest, - status::Custom( - Status::Unauthorized, - Json(Response { - message: String::from("Invalid token"), - data: serde_json::to_value("").unwrap(), - }), - ), - )) - } -} - -pub fn generate_token(login: LoginInfoDTO) -> String { - let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second - let payload = UserToken { - iat: now, - exp: now + ONE_WEEK, - user: login.username, - login_session: login.login_session, - }; - - jsonwebtoken::encode(&Header::default(), &payload, &EncodingKey::from_secret(include_bytes!("secret.key"))).unwrap() -} - -fn decode_token(token: String) -> Result> { - jsonwebtoken::decode::(&token, &DecodingKey::from_secret(include_bytes!("secret.key")), &Validation::default()) -} - -fn verify_token(token_data: &TokenData, conn: &DbConn) -> bool { - User::is_valid_login_session(&token_data.claims, conn) -} \ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index 0ebcef0..4f820ca 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -1,6 +1,7 @@ mod mutation; mod query; pub mod crypto; +pub mod token; pub use mutation::*; pub use query::*; diff --git a/core/src/token/admin_token.rs b/core/src/token/admin_token.rs new file mode 100644 index 0000000..73b7a5f --- /dev/null +++ b/core/src/token/admin_token.rs @@ -0,0 +1,9 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct AdminToken { + // issued at + pub iat: i64, + // expiration + pub exp: i64, +} \ No newline at end of file diff --git a/core/src/token/candidate_token.rs b/core/src/token/candidate_token.rs new file mode 100644 index 0000000..5a43493 --- /dev/null +++ b/core/src/token/candidate_token.rs @@ -0,0 +1,11 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct CandidateToken { + // issued at + pub iat: i64, + // expiration + pub exp: i64, + pub name: String, + pub surname: String, +} \ No newline at end of file diff --git a/core/src/token/mod.rs b/core/src/token/mod.rs new file mode 100644 index 0000000..53f9f0d --- /dev/null +++ b/core/src/token/mod.rs @@ -0,0 +1,67 @@ +pub mod admin_token; +pub mod candidate_token; + +use chrono::Utc; + +use entity::{admin, candidate}; +use jsonwebtoken::errors::Result; +use jsonwebtoken::TokenData; +use jsonwebtoken::{DecodingKey, EncodingKey}; +use jsonwebtoken::{Header, Validation}; + +use admin_token::AdminToken; +use candidate_token::CandidateToken; + +const ONE_WEEK: i64 = 60 * 60 * 24 * 7; + +pub fn generate_candidate_token(candidate: candidate::Model) -> String { + let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second + let payload = CandidateToken { + iat: now, + exp: now + ONE_WEEK, + name: candidate.name.unwrap_or_else(|| "".into()), + surname: candidate.surname.unwrap_or_else(|| "".into()), + }; + + jsonwebtoken::encode( + &Header::default(), + &payload, + &EncodingKey::from_secret(include_bytes!("secret.key")), + ) + .unwrap() +} + +pub fn generate_admin_token(_admin: admin::Model) -> String { + let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second + let payload = AdminToken { + iat: now, + exp: now + ONE_WEEK, + }; + + jsonwebtoken::encode( + &Header::default(), + &payload, + &EncodingKey::from_secret(include_bytes!("secret.key")), + ) + .unwrap() +} + +pub fn decode_candidate_token(token: String) -> Result> { + jsonwebtoken::decode::( + &token, + &DecodingKey::from_secret(include_bytes!("secret.key")), + &Validation::default(), + ) +} + +pub fn decode_admin_token(token: String) -> Result> { + jsonwebtoken::decode::( + &token, + &DecodingKey::from_secret(include_bytes!("secret.key")), + &Validation::default(), + ) +} + +/*pub fn verify_token(token_data: &TokenData, conn: &DbConn) -> bool { + User::is_valid_login_session(&token_data.claims, conn) +}*/ diff --git a/core/src/token/secret.key b/core/src/token/secret.key new file mode 100644 index 0000000..3602361 --- /dev/null +++ b/core/src/token/secret.key @@ -0,0 +1 @@ +temp \ No newline at end of file