From a3e87e0b27e4eb598bd9263b6ee4a3339b211ccf Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 14:51:08 +0200 Subject: [PATCH 01/22] chore: jsonwebtoken dependency --- core/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/core/Cargo.toml b/core/Cargo.toml index 4b41cec..4a05e7e 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -8,6 +8,7 @@ portfolio-entity = { path = "../entity" } rand = "0.8.5" argon2 = "0.4.1" chrono = "0.4.22" +jsonwebtoken = "8.1.1" [dependencies.sea-orm] version = "^0.10.0" From e059719172642007dd4cc226d58184d3dda88717 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 14:52:18 +0200 Subject: [PATCH 02/22] feat: gen, decode, verify jwt --- core/src/jwt.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 core/src/jwt.rs diff --git a/core/src/jwt.rs b/core/src/jwt.rs new file mode 100644 index 0000000..2b6675e --- /dev/null +++ b/core/src/jwt.rs @@ -0,0 +1,77 @@ +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 From 44b4310ddf0b423c84179f1f2a04a6553da6b719 Mon Sep 17 00:00:00 2001 From: EETagent Date: Tue, 25 Oct 2022 15:50:46 +0200 Subject: [PATCH 03/22] =?UTF-8?q?feat:=20token=20vylep=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/src/guard/candidate_jwt.rs | 39 ++++++++++++++++ core/Cargo.toml | 2 + core/src/jwt.rs | 77 ------------------------------- core/src/lib.rs | 1 + core/src/token/admin_token.rs | 9 ++++ core/src/token/candidate_token.rs | 11 +++++ core/src/token/mod.rs | 67 +++++++++++++++++++++++++++ core/src/token/secret.key | 1 + 8 files changed, 130 insertions(+), 77 deletions(-) create mode 100644 api/src/guard/candidate_jwt.rs delete mode 100644 core/src/jwt.rs create mode 100644 core/src/token/admin_token.rs create mode 100644 core/src/token/candidate_token.rs create mode 100644 core/src/token/mod.rs create mode 100644 core/src/token/secret.key 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 From 9eaf0eb3475ba691d4159893c7e28ccadb73bc5c Mon Sep 17 00:00:00 2001 From: EETagent Date: Tue, 25 Oct 2022 16:40:29 +0200 Subject: [PATCH 04/22] fix: mod importy --- api/src/guard/candidate_jwt.rs | 1 + api/src/guard/mod.rs | 1 + api/src/lib.rs | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 api/src/guard/mod.rs diff --git a/api/src/guard/candidate_jwt.rs b/api/src/guard/candidate_jwt.rs index 2cba64b..576f111 100644 --- a/api/src/guard/candidate_jwt.rs +++ b/api/src/guard/candidate_jwt.rs @@ -5,6 +5,7 @@ use rocket::request::{self, FromRequest, Request}; use rocket::response::status; use rocket_contrib::json::Json; +use portfolio_core::token::candidate_token::CandidateToken; use portfolio_core::token::decode_candidate_token; impl<'a, 'r> FromRequest<'a, 'r> for UserToken { diff --git a/api/src/guard/mod.rs b/api/src/guard/mod.rs new file mode 100644 index 0000000..f2318e8 --- /dev/null +++ b/api/src/guard/mod.rs @@ -0,0 +1 @@ +pub mod candidate_jwt; \ No newline at end of file diff --git a/api/src/lib.rs b/api/src/lib.rs index 5ba1fbd..a601693 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -9,7 +9,10 @@ use portfolio_core::{Mutation}; use migration::{MigratorTrait}; use sea_orm_rocket::{Connection, Database}; + mod pool; +mod guard; + use pool::Db; pub use entity::candidate; From 4a852fa915226d43301d17a9f2bd9cf4a5cfe2bc Mon Sep 17 00:00:00 2001 From: EETagent Date: Tue, 25 Oct 2022 16:44:08 +0200 Subject: [PATCH 05/22] refactor: dry decode token --- core/src/token/mod.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/core/src/token/mod.rs b/core/src/token/mod.rs index 53f9f0d..e6df692 100644 --- a/core/src/token/mod.rs +++ b/core/src/token/mod.rs @@ -11,6 +11,7 @@ use jsonwebtoken::{Header, Validation}; use admin_token::AdminToken; use candidate_token::CandidateToken; +use serde::Deserialize; const ONE_WEEK: i64 = 60 * 60 * 24 * 7; @@ -46,20 +47,20 @@ pub fn generate_admin_token(_admin: admin::Model) -> String { .unwrap() } -pub fn decode_candidate_token(token: String) -> Result> { - jsonwebtoken::decode::( +pub fn decode_token Deserialize<'a>>(token: String) -> Result> { + jsonwebtoken::decode::( &token, &DecodingKey::from_secret(include_bytes!("secret.key")), &Validation::default(), ) } +pub fn decode_candidate_token(token: String) -> Result> { + decode_token(token) +} + pub fn decode_admin_token(token: String) -> Result> { - jsonwebtoken::decode::( - &token, - &DecodingKey::from_secret(include_bytes!("secret.key")), - &Validation::default(), - ) + decode_token(token) } /*pub fn verify_token(token_data: &TokenData, conn: &DbConn) -> bool { From 32c266e3663a978e0105dbc9d6280b8524ae7ed7 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 17:35:03 +0200 Subject: [PATCH 06/22] feat: user login service --- core/src/lib.rs | 1 + core/src/services/candidate_service.rs | 28 ++++++++++++++++++++++++++ core/src/services/mod.rs | 1 + core/src/token/candidate_token.rs | 13 ++++++++++++ entity/src/mod.rs | 8 ++++++++ 5 files changed, 51 insertions(+) create mode 100644 core/src/services/candidate_service.rs create mode 100644 core/src/services/mod.rs create mode 100644 entity/src/mod.rs diff --git a/core/src/lib.rs b/core/src/lib.rs index 4f820ca..942af37 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -2,6 +2,7 @@ mod mutation; mod query; pub mod crypto; pub mod token; +pub mod services; pub use mutation::*; pub use query::*; diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs new file mode 100644 index 0000000..dffb5ba --- /dev/null +++ b/core/src/services/candidate_service.rs @@ -0,0 +1,28 @@ +use jsonwebtoken::{Header, EncodingKey}; +use sea_orm::DatabaseConnection; + +use crate::{crypto, Query, token::candidate_token::CandidateToken}; + +pub async fn login(db: &DatabaseConnection, id: i32, password: String) -> Option { + let candidate = Query::find_candidate_by_id(db, id).await + .unwrap() + .unwrap(); + + + let valid = crypto::verify_password(&password,&candidate.code ) + .expect("Invalid password"); + + if !valid { + return None; + } + let payload = CandidateToken::generate(candidate.name.unwrap(), + candidate.surname.unwrap()); + + let jwt = jsonwebtoken::encode( + &Header::default(), + &payload, + &EncodingKey::from_secret(&[0]) + ).ok(); + jwt +} + diff --git a/core/src/services/mod.rs b/core/src/services/mod.rs new file mode 100644 index 0000000..1992ce2 --- /dev/null +++ b/core/src/services/mod.rs @@ -0,0 +1 @@ +pub mod candidate_service; \ No newline at end of file diff --git a/core/src/token/candidate_token.rs b/core/src/token/candidate_token.rs index 5a43493..7c5a973 100644 --- a/core/src/token/candidate_token.rs +++ b/core/src/token/candidate_token.rs @@ -1,3 +1,4 @@ +use chrono::Utc; use serde::{Serialize, Deserialize}; #[derive(Debug, Serialize, Deserialize)] @@ -8,4 +9,16 @@ pub struct CandidateToken { pub exp: i64, pub name: String, pub surname: String, +} + +impl CandidateToken { + pub fn generate(name: String, surname: String) -> Self { + let now = Utc::now().timestamp(); + CandidateToken { + iat: now, + exp: now + 60 * 60, // 1 hour for now + name, + surname, + } + } } \ No newline at end of file diff --git a/entity/src/mod.rs b/entity/src/mod.rs new file mode 100644 index 0000000..63cf621 --- /dev/null +++ b/entity/src/mod.rs @@ -0,0 +1,8 @@ +//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3 + +pub mod prelude; + +pub mod admin; +pub mod candidate; +pub mod parent; +pub mod session; From 7a9d59a60c9a68bfe55d05821642a2e3199ab43f Mon Sep 17 00:00:00 2001 From: EETagent Date: Tue, 25 Oct 2022 18:06:10 +0200 Subject: [PATCH 07/22] fix: fix build --- api/src/guard/candidate_jwt.rs | 41 +++++++++++----------------------- api/src/lib.rs | 1 + 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/api/src/guard/candidate_jwt.rs b/api/src/guard/candidate_jwt.rs index 576f111..977f021 100644 --- a/api/src/guard/candidate_jwt.rs +++ b/api/src/guard/candidate_jwt.rs @@ -1,40 +1,25 @@ -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 rocket::request::{FromRequest, Request}; use portfolio_core::token::candidate_token::CandidateToken; 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(); +pub struct Token(CandidateToken); + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Token { + type Error = Status; + async fn from_request(req: &'r Request<'_>) -> Outcome { + if let Some(auth) = req.headers().get_one("Authorization") { + let auth_string = auth.to_string(); + if auth_string.starts_with("Bearer") { + let token = auth_string[6..auth_string.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); - // } + return Outcome::Success(Token(token_data.claims)); } } } - - Outcome::Failure(( - Status::BadRequest, - status::Custom( - Status::Unauthorized, - Json(Response { - message: String::from("Invalid token"), - data: serde_json::to_value("").unwrap(), - }), - ), - )) + return Outcome::Failure((Status::Unauthorized, Status::Unauthorized)); } } diff --git a/api/src/lib.rs b/api/src/lib.rs index a601693..9b245e2 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -1,5 +1,6 @@ #[macro_use] extern crate rocket; + use rocket::{Rocket, Build}; use rocket::serde::json::Json; use rocket::fairing::{self, AdHoc}; From 3c4331f9661ee7d15dce960dd65011d2c8b745c9 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 18:20:49 +0200 Subject: [PATCH 08/22] refactor: token request --- api/src/guard/candidate_jwt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/guard/candidate_jwt.rs b/api/src/guard/candidate_jwt.rs index 977f021..2a96684 100644 --- a/api/src/guard/candidate_jwt.rs +++ b/api/src/guard/candidate_jwt.rs @@ -5,18 +5,18 @@ use rocket::request::{FromRequest, Request}; use portfolio_core::token::candidate_token::CandidateToken; use portfolio_core::token::decode_candidate_token; -pub struct Token(CandidateToken); +pub struct TokenRequest(CandidateToken); #[rocket::async_trait] -impl<'r> FromRequest<'r> for Token { +impl<'r> FromRequest<'r> for TokenRequest { type Error = Status; - async fn from_request(req: &'r Request<'_>) -> Outcome { + async fn from_request(req: &'r Request<'_>) -> Outcome { if let Some(auth) = req.headers().get_one("Authorization") { let auth_string = auth.to_string(); if auth_string.starts_with("Bearer") { let token = auth_string[6..auth_string.len()].trim(); if let Ok(token_data) = decode_candidate_token(token.to_string()) { - return Outcome::Success(Token(token_data.claims)); + return Outcome::Success(TokenRequest(token_data.claims)); } } } From 951c5de04f1916f43fa5d683d50bb9601ba2f473 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 18:41:43 +0200 Subject: [PATCH 09/22] feat: to_token --- api/src/guard/candidate_jwt.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/guard/candidate_jwt.rs b/api/src/guard/candidate_jwt.rs index 2a96684..65ef832 100644 --- a/api/src/guard/candidate_jwt.rs +++ b/api/src/guard/candidate_jwt.rs @@ -7,6 +7,12 @@ use portfolio_core::token::decode_candidate_token; pub struct TokenRequest(CandidateToken); +impl TokenRequest { + pub fn to_token(self) -> CandidateToken { + self.0 + } +} + #[rocket::async_trait] impl<'r> FromRequest<'r> for TokenRequest { type Error = Status; From 28686a127af9f09c0d3231abb9da1107615e8f3b Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 18:42:31 +0200 Subject: [PATCH 10/22] feat: login --- api/src/lib.rs | 28 +++++++++++++++++- api/src/requests.rs | 9 ++++++ core/src/services/candidate_service.rs | 40 ++++++++++++++------------ 3 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 api/src/requests.rs diff --git a/api/src/lib.rs b/api/src/lib.rs index 9b245e2..ab73f40 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -1,6 +1,9 @@ #[macro_use] extern crate rocket; +use guard::candidate_jwt::TokenRequest; +use portfolio_core::services::candidate_service::CandidateService; +use requests::LoginRequest; use rocket::{Rocket, Build}; use rocket::serde::json::Json; use rocket::fairing::{self, AdHoc}; @@ -13,6 +16,7 @@ use sea_orm_rocket::{Connection, Database}; mod pool; mod guard; +mod requests; use pool::Db; @@ -36,6 +40,28 @@ async fn create(conn: Connection<'_, Db>, post_form: Json) -> Ok(plain_text_password) } +#[post("/login", data = "")] +async fn login(conn: Connection<'_, Db>, login_form: Json) -> Result> { + let db = conn.into_inner(); + println!("{} {}", login_form.application_id, login_form.password); + + let jwt = CandidateService::login(db, + login_form.application_id, + login_form.password.to_owned()).await; + + if jwt.is_some() { + return Ok(jwt.unwrap()) + } + Ok("jwt here".to_owned()) +} + +#[get("/whoami")] +async fn whoami(token: TokenRequest) -> Result> { + println!("{:?}", token.to_token()); + + Ok("authenticated!".to_owned()) +} + #[get("/hello")] async fn hello() -> &'static str { "Hello, world!" @@ -53,7 +79,7 @@ async fn start() -> Result<(), rocket::Error> { .attach(Db::init()) .attach(AdHoc::try_on_ignite("Migrations", run_migrations)) //.mount("/", FileServer::from(relative!("/static"))) - .mount("/", routes![create, hello]) + .mount("/", routes![create, login, hello, whoami]) .register("/", catchers![]) .launch() .await diff --git a/api/src/requests.rs b/api/src/requests.rs new file mode 100644 index 0000000..9c86d49 --- /dev/null +++ b/api/src/requests.rs @@ -0,0 +1,9 @@ +use rocket::serde::{Serialize, Deserialize}; + + +#[derive(Serialize, Deserialize)] +#[serde(crate = "rocket::serde")] +pub struct LoginRequest { + pub application_id: i32, + pub password: String, +} \ No newline at end of file diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index dffb5ba..4ccd003 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -3,26 +3,30 @@ use sea_orm::DatabaseConnection; use crate::{crypto, Query, token::candidate_token::CandidateToken}; -pub async fn login(db: &DatabaseConnection, id: i32, password: String) -> Option { - let candidate = Query::find_candidate_by_id(db, id).await - .unwrap() - .unwrap(); +pub struct CandidateService; +impl CandidateService { + pub async fn login(db: &DatabaseConnection, id: i32, password: String) -> Option { + let candidate = Query::find_candidate_by_id(db, id).await + .unwrap() + .unwrap(); - let valid = crypto::verify_password(&password,&candidate.code ) - .expect("Invalid password"); + + let valid = crypto::verify_password(&password,&candidate.code ) + .expect("Invalid password"); + + if !valid { + return None; + } + let payload = CandidateToken::generate("candidate.name.unwrap()".to_owned(), + "candidate.surname.unwrap()".to_owned()); - if !valid { - return None; + let jwt = jsonwebtoken::encode( + &Header::default(), + &payload, + &EncodingKey::from_secret(&[0]) + ).ok(); + jwt } - let payload = CandidateToken::generate(candidate.name.unwrap(), - candidate.surname.unwrap()); - - let jwt = jsonwebtoken::encode( - &Header::default(), - &payload, - &EncodingKey::from_secret(&[0]) - ).ok(); - jwt -} +} From 129b0f198ff15b02dda1b8de47d8916d63ed8c5c Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 18:55:39 +0200 Subject: [PATCH 11/22] refactor: error handling --- api/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index ab73f40..26e2210 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -4,6 +4,7 @@ extern crate rocket; use guard::candidate_jwt::TokenRequest; use portfolio_core::services::candidate_service::CandidateService; use requests::LoginRequest; +use rocket::http::Status; use rocket::{Rocket, Build}; use rocket::serde::json::Json; use rocket::fairing::{self, AdHoc}; @@ -52,7 +53,7 @@ async fn login(conn: Connection<'_, Db>, login_form: Json) -> Resu if jwt.is_some() { return Ok(jwt.unwrap()) } - Ok("jwt here".to_owned()) + Err(Custom(Status::Unauthorized, "Invalid credentials".to_string())) } #[get("/whoami")] From 4ab6f16774352af1130e7480dfbc41dd51050e4d Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 19:07:55 +0200 Subject: [PATCH 12/22] feat: errors --- core/src/error.rs | 9 +++++++++ core/src/lib.rs | 1 + 2 files changed, 10 insertions(+) create mode 100644 core/src/error.rs diff --git a/core/src/error.rs b/core/src/error.rs new file mode 100644 index 0000000..a76d2ec --- /dev/null +++ b/core/src/error.rs @@ -0,0 +1,9 @@ +pub struct Status { + pub code: u16, +} + +pub const InvalidCredentialsError: ServiceError = ServiceError(Status { code: 401 }, + "Invalid credentials".to_string()); +pub const JwtError: ServiceError = ServiceError(Status { code: 500 }, + "Error while encoding JWT".to_string()); +pub struct ServiceError(pub Status, pub R); \ No newline at end of file diff --git a/core/src/lib.rs b/core/src/lib.rs index 942af37..71b5ace 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -3,6 +3,7 @@ mod query; pub mod crypto; pub mod token; pub mod services; +pub mod error; pub use mutation::*; pub use query::*; From 497345f2e9ec8839075ff0bcb633876a77079364 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 19:34:30 +0200 Subject: [PATCH 13/22] feat: improve error handling --- api/src/lib.rs | 18 +++++++++++++---- core/src/error.rs | 16 ++++++++++----- core/src/services/candidate_service.rs | 27 +++++++++++++++++--------- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 26e2210..004f83a 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -2,10 +2,11 @@ extern crate rocket; use guard::candidate_jwt::TokenRequest; +use portfolio_core::error::ServiceError; use portfolio_core::services::candidate_service::CandidateService; use requests::LoginRequest; use rocket::http::Status; -use rocket::{Rocket, Build}; +use rocket::{Rocket, Build, custom}; use rocket::serde::json::Json; use rocket::fairing::{self, AdHoc}; use rocket::response::status::Custom; @@ -27,6 +28,10 @@ pub use entity::candidate::Entity as Candidate; use portfolio_core::crypto::random_8_char_string; +fn custom_err_from_service_err(err: ServiceError) -> Custom { + Custom(Status::InternalServerError, err.1.to_string()) +} + #[post("/", data = "")] async fn create(conn: Connection<'_, Db>, post_form: Json) -> Result> { let db = conn.into_inner(); @@ -50,10 +55,15 @@ async fn login(conn: Connection<'_, Db>, login_form: Json) -> Resu login_form.application_id, login_form.password.to_owned()).await; - if jwt.is_some() { - return Ok(jwt.unwrap()) + if jwt.is_ok() { + return Ok( + jwt.ok().unwrap() + ); + } else { + return Err( + custom_err_from_service_err(jwt.err().unwrap()) + ) } - Err(Custom(Status::Unauthorized, "Invalid credentials".to_string())) } #[get("/whoami")] diff --git a/core/src/error.rs b/core/src/error.rs index a76d2ec..36cb105 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -2,8 +2,14 @@ pub struct Status { pub code: u16, } -pub const InvalidCredentialsError: ServiceError = ServiceError(Status { code: 401 }, - "Invalid credentials".to_string()); -pub const JwtError: ServiceError = ServiceError(Status { code: 500 }, - "Error while encoding JWT".to_string()); -pub struct ServiceError(pub Status, pub R); \ No newline at end of file +pub const INVALID_CREDENTIALS_ERROR: ServiceError = ServiceError(Status { code: 401 }, + "Invalid credentials"); +pub const JWT_ERROR: ServiceError = ServiceError(Status { code: 500 }, + "Error while encoding JWT"); + +pub const USER_NOT_FOUND_ERROR: ServiceError = ServiceError(Status { code: 404 }, + "User not found"); + +pub const DB_ERROR: ServiceError = ServiceError(Status { code: 500 }, + "Database error"); +pub struct ServiceError<'a>(pub Status, pub &'a str); \ No newline at end of file diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 4ccd003..fb7eda4 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,23 +1,28 @@ use jsonwebtoken::{Header, EncodingKey}; use sea_orm::DatabaseConnection; -use crate::{crypto, Query, token::candidate_token::CandidateToken}; +use crate::{crypto, Query, token::candidate_token::CandidateToken, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, JWT_ERROR, DB_ERROR}}; pub struct CandidateService; impl CandidateService { - pub async fn login(db: &DatabaseConnection, id: i32, password: String) -> Option { - let candidate = Query::find_candidate_by_id(db, id).await - .unwrap() - .unwrap(); + pub async fn login(db: &DatabaseConnection, id: i32, password: String) -> Result { + let candidate = match Query::find_candidate_by_id(db, id).await { + Ok(candidate) => match candidate { + Some(candidate) => candidate, + None => return Err(USER_NOT_FOUND_ERROR) + }, + Err(_) => {return Err(DB_ERROR)} + }; let valid = crypto::verify_password(&password,&candidate.code ) .expect("Invalid password"); if !valid { - return None; + return Err(INVALID_CREDENTIALS_ERROR) } + let payload = CandidateToken::generate("candidate.name.unwrap()".to_owned(), "candidate.surname.unwrap()".to_owned()); @@ -26,7 +31,11 @@ impl CandidateService { &payload, &EncodingKey::from_secret(&[0]) ).ok(); - jwt + + match jwt { + Some(jwt) => Ok(jwt), + None => Err(JWT_ERROR) + } + + } } - -} From ad4fe123e416ea4add9a052a1d3160c0d5b57b16 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 21:43:15 +0200 Subject: [PATCH 14/22] feat: error codes --- api/src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 004f83a..57776a4 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -6,7 +6,7 @@ use portfolio_core::error::ServiceError; use portfolio_core::services::candidate_service::CandidateService; use requests::LoginRequest; use rocket::http::Status; -use rocket::{Rocket, Build, custom}; +use rocket::{Rocket, Build}; use rocket::serde::json::Json; use rocket::fairing::{self, AdHoc}; use rocket::response::status::Custom; @@ -27,9 +27,8 @@ pub use entity::candidate::Entity as Candidate; use portfolio_core::crypto::random_8_char_string; - -fn custom_err_from_service_err(err: ServiceError) -> Custom { - Custom(Status::InternalServerError, err.1.to_string()) +fn custom_err_from_service_err(service_err: ServiceError) -> Custom { + Custom(Status::from_code(service_err.0.code).unwrap_or_default(), service_err.1.to_string()) } #[post("/", data = "")] From a2d1447c2d07b6b5e3961bb1486646cb062c0a22 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Tue, 25 Oct 2022 22:00:48 +0200 Subject: [PATCH 15/22] refactor: use generate_candidate_token --- core/src/services/candidate_service.rs | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index fb7eda4..e94ccb1 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,7 +1,7 @@ use jsonwebtoken::{Header, EncodingKey}; use sea_orm::DatabaseConnection; -use crate::{crypto, Query, token::candidate_token::CandidateToken, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, JWT_ERROR, DB_ERROR}}; +use crate::{crypto, Query, token::{candidate_token::CandidateToken, generate_candidate_token}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, JWT_ERROR, DB_ERROR}}; pub struct CandidateService; @@ -23,19 +23,8 @@ impl CandidateService { return Err(INVALID_CREDENTIALS_ERROR) } - let payload = CandidateToken::generate("candidate.name.unwrap()".to_owned(), - "candidate.surname.unwrap()".to_owned()); - - let jwt = jsonwebtoken::encode( - &Header::default(), - &payload, - &EncodingKey::from_secret(&[0]) - ).ok(); - - match jwt { - Some(jwt) => Ok(jwt), - None => Err(JWT_ERROR) - } + let jwt = generate_candidate_token(candidate); // TODO better error handling + Ok(jwt) } } From 5d52cf7772da52787ee056f13546d88a53dd637b Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 10:28:19 +0200 Subject: [PATCH 16/22] refactor: code cleanup --- api/src/guard/candidate_jwt.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/api/src/guard/candidate_jwt.rs b/api/src/guard/candidate_jwt.rs index 65ef832..25d9486 100644 --- a/api/src/guard/candidate_jwt.rs +++ b/api/src/guard/candidate_jwt.rs @@ -21,8 +21,10 @@ impl<'r> FromRequest<'r> for TokenRequest { let auth_string = auth.to_string(); if auth_string.starts_with("Bearer") { let token = auth_string[6..auth_string.len()].trim(); - if let Ok(token_data) = decode_candidate_token(token.to_string()) { - return Outcome::Success(TokenRequest(token_data.claims)); + let token_data = decode_candidate_token(token.to_string()); + + if token_data.is_ok() { + return Outcome::Success(TokenRequest(token_data.ok().unwrap().claims)); } } } From 50706c329848f7bc1c9c030101ed1db81d183c92 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 10:30:17 +0200 Subject: [PATCH 17/22] fix: unused imports --- core/src/services/candidate_service.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index e94ccb1..5862133 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,7 +1,6 @@ -use jsonwebtoken::{Header, EncodingKey}; use sea_orm::DatabaseConnection; -use crate::{crypto, Query, token::{candidate_token::CandidateToken, generate_candidate_token}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, JWT_ERROR, DB_ERROR}}; +use crate::{crypto, Query, token::{generate_candidate_token}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR}}; pub struct CandidateService; From 2d4eb87d27963c5d4728b83e55f550d1e5dadff6 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 10:30:28 +0200 Subject: [PATCH 18/22] feat: test --- core/src/token/mod.rs | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/core/src/token/mod.rs b/core/src/token/mod.rs index e6df692..52825e7 100644 --- a/core/src/token/mod.rs +++ b/core/src/token/mod.rs @@ -16,7 +16,7 @@ use serde::Deserialize; 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 now = Utc::now().timestamp(); let payload = CandidateToken { iat: now, exp: now + ONE_WEEK, @@ -33,7 +33,7 @@ pub fn generate_candidate_token(candidate: candidate::Model) -> String { } pub fn generate_admin_token(_admin: admin::Model) -> String { - let now = Utc::now().timestamp_nanos() / 1_000_000_000; // nanosecond -> second + let now = Utc::now().timestamp(); let payload = AdminToken { iat: now, exp: now + ONE_WEEK, @@ -63,6 +63,35 @@ pub fn decode_admin_token(token: String) -> Result> { decode_token(token) } -/*pub fn verify_token(token_data: &TokenData, conn: &DbConn) -> bool { - User::is_valid_login_session(&token_data.claims, conn) -}*/ + +#[test] +fn test_encode_decode_verify_token() { + let candidate_model = candidate::Model { + application: 101204, + code: "random_code".to_string(), + birth_surname: None, + birthplace: None, + birthdate: None, + address: None, + telephone: None, + citizenship: None, + sex: None, + study: None, + personal_identification_number: None, + personal_identification_number_hash: None, + public_key: "None".to_owned(), + private_key: "None".to_owned(), + created_at: Utc::now().naive_local(), + updated_at: Utc::now().naive_local(), + name: Some("Uplnej".to_string()), + surname: Some("Magor".to_string()), + email: Some("email.uchazece@centrum.cz".to_string()), + }; + + let jwt = generate_candidate_token(candidate_model.clone()); + + let decoded = decode_candidate_token(jwt).unwrap(); + let token_claims = decoded.claims; + assert_eq!(candidate_model.name.unwrap(), token_claims.name); + assert_eq!(candidate_model.surname.unwrap(), token_claims.surname); +} \ No newline at end of file From e3c5052ffc01db4f13f5e1504aeea94f2e76a94f Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 10:36:31 +0200 Subject: [PATCH 19/22] refactor: test name --- core/src/token/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/token/mod.rs b/core/src/token/mod.rs index 52825e7..af07dbd 100644 --- a/core/src/token/mod.rs +++ b/core/src/token/mod.rs @@ -65,7 +65,7 @@ pub fn decode_admin_token(token: String) -> Result> { #[test] -fn test_encode_decode_verify_token() { +fn test_encode_decode_token() { let candidate_model = candidate::Model { application: 101204, code: "random_code".to_string(), From 04bc69501f90cf739cb5be7c147551d6412e9360 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 10:55:53 +0200 Subject: [PATCH 20/22] feat: application_id in jwt --- core/src/token/candidate_token.rs | 4 +++- core/src/token/mod.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/token/candidate_token.rs b/core/src/token/candidate_token.rs index 7c5a973..7014965 100644 --- a/core/src/token/candidate_token.rs +++ b/core/src/token/candidate_token.rs @@ -7,16 +7,18 @@ pub struct CandidateToken { pub iat: i64, // expiration pub exp: i64, + pub application_id: i32, pub name: String, pub surname: String, } impl CandidateToken { - pub fn generate(name: String, surname: String) -> Self { + pub fn generate(application_id: i32, name: String, surname: String) -> Self { let now = Utc::now().timestamp(); CandidateToken { iat: now, exp: now + 60 * 60, // 1 hour for now + application_id, name, surname, } diff --git a/core/src/token/mod.rs b/core/src/token/mod.rs index af07dbd..370f28f 100644 --- a/core/src/token/mod.rs +++ b/core/src/token/mod.rs @@ -20,6 +20,7 @@ pub fn generate_candidate_token(candidate: candidate::Model) -> String { let payload = CandidateToken { iat: now, exp: now + ONE_WEEK, + application_id: candidate.application, name: candidate.name.unwrap_or_else(|| "".into()), surname: candidate.surname.unwrap_or_else(|| "".into()), }; From ecc9b54ce506ae6224ad474b07f1cf545dee0ae1 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 11:22:42 +0200 Subject: [PATCH 21/22] feat: authenticate user from jwt --- api/src/lib.rs | 11 ++++++++--- core/src/error.rs | 3 +++ core/src/services/candidate_service.rs | 18 ++++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 57776a4..b4d16c9 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -66,10 +66,15 @@ async fn login(conn: Connection<'_, Db>, login_form: Json) -> Resu } #[get("/whoami")] -async fn whoami(token: TokenRequest) -> Result> { - println!("{:?}", token.to_token()); +async fn whoami(conn: Connection<'_, Db>, token_req: Result) -> Result> { + let db = conn.into_inner(); + let token = token_req.ok().unwrap().to_token(); + let user = CandidateService::authenticate_candidate(db, token).await; - Ok("authenticated!".to_owned()) + match user { + Ok(user) => Ok(format!("{} {}", user.name.unwrap(), user.surname.unwrap())), + Err(e) => Err(custom_err_from_service_err(e)), + } } #[get("/hello")] diff --git a/core/src/error.rs b/core/src/error.rs index 36cb105..b2093a7 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -12,4 +12,7 @@ pub const USER_NOT_FOUND_ERROR: ServiceError = ServiceError(Status { code: 404 } pub const DB_ERROR: ServiceError = ServiceError(Status { code: 500 }, "Database error"); + +pub const USER_NOT_FOUND_BY_JWT_ID: ServiceError = ServiceError(Status { code: 500 }, // User got somehow + "User not found, please contact technical support"); // Shouldn't ever happen pub struct ServiceError<'a>(pub Status, pub &'a str); \ No newline at end of file diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 5862133..3df4db3 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,10 +1,12 @@ +use entity::candidate; use sea_orm::DatabaseConnection; -use crate::{crypto, Query, token::{generate_candidate_token}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR}}; +use crate::{crypto, Query, token::{generate_candidate_token, decode_candidate_token, candidate_token::CandidateToken}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR, JWT_ERROR, USER_NOT_FOUND_BY_JWT_ID}}; pub struct CandidateService; impl CandidateService { + pub async fn login(db: &DatabaseConnection, id: i32, password: String) -> Result { let candidate = match Query::find_candidate_by_id(db, id).await { Ok(candidate) => match candidate { @@ -25,5 +27,17 @@ impl CandidateService { let jwt = generate_candidate_token(candidate); // TODO better error handling Ok(jwt) - } } + + pub async fn authenticate_candidate(db: &DatabaseConnection, token: CandidateToken) -> Result { + let candidate = match Query::find_candidate_by_id(db, token.application_id).await { + Ok(candidate) => match candidate { + Some(candidate) => candidate, + None => return Err(USER_NOT_FOUND_BY_JWT_ID) + }, + Err(_) => {return Err(DB_ERROR)} + }; + + Ok(candidate) + } +} From 6a425d3f7a9254dc26848d2f1adc5a20fe2fbd1c Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Wed, 26 Oct 2022 11:23:24 +0200 Subject: [PATCH 22/22] fix: imports --- core/src/services/candidate_service.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 3df4db3..3ba2e8b 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -1,7 +1,7 @@ use entity::candidate; use sea_orm::DatabaseConnection; -use crate::{crypto, Query, token::{generate_candidate_token, decode_candidate_token, candidate_token::CandidateToken}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR, JWT_ERROR, USER_NOT_FOUND_BY_JWT_ID}}; +use crate::{crypto, Query, token::{generate_candidate_token, candidate_token::CandidateToken}, error::{ServiceError, USER_NOT_FOUND_ERROR, INVALID_CREDENTIALS_ERROR, DB_ERROR, USER_NOT_FOUND_BY_JWT_ID}}; pub struct CandidateService;