Merge pull request #34 from EETagent/routing

Routing
This commit is contained in:
Vojtěch Jungmann 2022-11-07 16:33:32 +01:00 committed by GitHub
commit 077adfd117
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 213 additions and 83 deletions

View file

@ -7,6 +7,7 @@ publish = false
[dependencies]
rocket = { version = "^0.5.0-rc.2", features = [
"json",
"secrets",
] }
async-stream = { version = "^0.3" }

View file

@ -19,7 +19,14 @@ impl Into<Admin> for AdminAuth {
impl<'r> FromRequest<'r> for AdminAuth {
type Error = Option<String>;
async fn from_request(req: &'r Request<'_>) -> Outcome<AdminAuth, (Status, Self::Error), ()> {
let session_id = req.cookies().get("id").unwrap().name_value().1;
let cookie = req.cookies().get_private("id");
let Some(cookie) = cookie else {
return Outcome::Failure((Status::Unauthorized, None));
};
let session_id = cookie.name_value().1;
let conn = &req.rocket().state::<Db>().unwrap().conn;
let uuid = match Uuid::parse_str(&session_id) {

View file

@ -14,12 +14,21 @@ impl Into<Candidate> for CandidateAuth {
self.0
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for CandidateAuth {
type Error = Option<String>;
async fn from_request(req: &'r Request<'_>) -> Outcome<CandidateAuth, (Status, Self::Error), ()> {
let session_id = req.cookies().get("id").unwrap().name_value().1;
async fn from_request(
req: &'r Request<'_>,
) -> Outcome<CandidateAuth, (Status, Self::Error), ()> {
let cookie = req.cookies().get_private("id");
let Some(cookie) = cookie else {
return Outcome::Failure((Status::Unauthorized, None));
};
let session_id = cookie.name_value().1;
let conn = &req.rocket().state::<Db>().unwrap().conn;
let uuid = match Uuid::parse_str(&session_id) {
@ -33,6 +42,5 @@ impl<'r> FromRequest<'r> for CandidateAuth {
Ok(model) => Outcome::Success(CandidateAuth(model)),
Err(_) => Outcome::Failure((Status::Unauthorized, None)),
}
}
}
}

View file

@ -1,23 +1,15 @@
#[macro_use]
extern crate rocket;
use std::net::SocketAddr;
use guards::request::auth::{CandidateAuth, AdminAuth};
use portfolio_core::services::candidate_service::{CandidateService, UserDetails};
use requests::{LoginRequest, RegisterRequest};
use rocket::http::Status;
use rocket::{Rocket, Build};
use rocket::serde::json::Json;
use rocket::fairing::{self, AdHoc};
use rocket::response::status::Custom;
use migration::{MigratorTrait};
use sea_orm_rocket::{Connection, Database};
use rocket::{Build, Rocket};
use migration::MigratorTrait;
use sea_orm_rocket::Database;
mod pool;
mod guards;
mod pool;
mod requests;
mod routes;
@ -26,70 +18,6 @@ use pool::Db;
pub use entity::candidate;
pub use entity::candidate::Entity as Candidate;
use portfolio_core::crypto::random_8_char_string;
#[post("/", data = "<post_form>")]
async fn create(conn: Connection<'_, Db>, post_form: Json<RegisterRequest>) -> Result<String, Custom<String>> {
let db = conn.into_inner();
let form = post_form.into_inner();
let plain_text_password = random_8_char_string();
let candidate = CandidateService::create(db, form.application_id, &plain_text_password, form.personal_id_number)
.await;
if candidate.is_err() { // TODO cleanup
let e = candidate.err().unwrap();
return Err(Custom(Status::from_code(e.code()).unwrap_or_default(), e.message()));
}
Ok(plain_text_password)
}
#[get("/whoami")]
async fn validate(session: CandidateAuth) -> Result<String, Custom<String>> {
let candidate: entity::candidate::Model = session.into();
Ok(candidate.application.to_string())
}
#[get("/admin")]
async fn admin(session: AdminAuth) -> Result<String, Custom<String>> {
Ok("Hello admin".to_string())
}
#[put("/details", data = "<details>")]
async fn fill_details(conn: Connection<'_, Db>, details: Json<UserDetails>, session: CandidateAuth) -> Result<String, Custom<String>> {
let db = conn.into_inner();
let form = details.into_inner();
let candidate: entity::candidate::Model = session.into();
let candidate = CandidateService::add_user_details(db, candidate, form)
.await;
if candidate.is_err() { // TODO cleanup
let e = candidate.err().unwrap();
return Err(Custom(Status::from_code(e.code()).unwrap_or_default(), e.message()));
}
Ok("Details added".to_string())
}
#[post("/login", data = "<login_form>")]
async fn login(conn: Connection<'_, Db>, login_form: Json<LoginRequest>, ip_addr: SocketAddr) -> Result<String, Custom<String>> {
let db = conn.into_inner();
println!("{} {}", login_form.application_id, login_form.password);
let session_token = CandidateService::login(
db,
login_form.application_id,
login_form.password.to_string(),
ip_addr.ip().to_string()
)
.await;
session_token.map_err(|e| Custom(Status::from_code(e.code()).unwrap_or_default(), e.message()))
}
#[get("/hello")]
async fn hello() -> &'static str {
"Hello, world!"
@ -107,7 +35,24 @@ 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, login, hello, validate, fill_details, admin])
.mount("/", routes![hello])
.mount(
"/candidate/",
routes![
routes::candidate::login,
routes::candidate::whoami,
routes::candidate::fill_details,
],
)
.mount(
"/admin/",
routes![
routes::admin::login,
routes::admin::whoami,
routes::admin::hello,
routes::admin::create_candidate,
],
)
.register("/", catchers![])
.launch()
.await

View file

@ -13,4 +13,12 @@ pub struct LoginRequest {
pub struct RegisterRequest {
pub application_id: i32,
pub personal_id_number: String,
}
#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
pub struct AdminLoginRequest {
pub admin_id: i32,
pub password: String,
}

87
api/src/routes/admin.rs Normal file
View file

@ -0,0 +1,87 @@
use std::net::SocketAddr;
use portfolio_core::{
crypto::random_8_char_string,
services::{admin_service::AdminService, candidate_service::CandidateService},
};
use requests::{AdminLoginRequest, RegisterRequest};
use rocket::http::{Cookie, Status, CookieJar};
use rocket::response::status::Custom;
use rocket::serde::json::Json;
use sea_orm_rocket::Connection;
use crate::{guards::request::auth::AdminAuth, pool::Db, requests};
#[post("/login", data = "<login_form>")]
pub async fn login(
conn: Connection<'_, Db>,
login_form: Json<AdminLoginRequest>,
ip_addr: SocketAddr,
cookies: &CookieJar<'_>,
) -> Result<String, Custom<String>> {
let db = conn.into_inner();
println!("{} {}", login_form.admin_id, login_form.password);
let session_token = AdminService::login(
db,
login_form.admin_id,
login_form.password.to_string(),
ip_addr.ip().to_string(),
)
.await;
if let Err(e) = session_token {
return Err(Custom(
Status::from_code(e.code()).unwrap_or(Status::InternalServerError),
e.to_string(),
));
} else {
let session_token = session_token.unwrap();
cookies.add_private(Cookie::new("id", session_token.clone()));
return Ok(session_token);
}
}
#[get("/whoami")]
pub async fn whoami(session: AdminAuth) -> Result<String, Custom<String>> {
let admin: entity::admin::Model = session.into();
Ok(admin.id.to_string())
}
#[get("/hello")]
pub async fn hello(_session: AdminAuth) -> Result<String, Custom<String>> {
Ok("Hello admin".to_string())
}
#[post("/create", data = "<post_form>")]
pub async fn create_candidate(
conn: Connection<'_, Db>,
_session: AdminAuth,
post_form: Json<RegisterRequest>,
) -> Result<String, Custom<String>> {
let db = conn.into_inner();
let form = post_form.into_inner();
let plain_text_password = random_8_char_string();
let candidate = CandidateService::create(
db,
form.application_id,
&plain_text_password,
form.personal_id_number,
)
.await;
if candidate.is_err() {
// TODO cleanup
let e = candidate.err().unwrap();
return Err(Custom(
Status::from_code(e.code()).unwrap_or_default(),
e.message(),
));
}
Ok(plain_text_password)
}

View file

@ -0,0 +1,72 @@
use std::net::SocketAddr;
use portfolio_core::services::candidate_service::{CandidateService, UserDetails};
use requests::LoginRequest;
use rocket::http::{Cookie, CookieJar, Status};
use rocket::response::status::Custom;
use rocket::serde::json::Json;
use sea_orm_rocket::Connection;
use crate::{guards::request::auth::CandidateAuth, pool::Db, requests};
#[post("/login", data = "<login_form>")]
pub async fn login(
conn: Connection<'_, Db>,
login_form: Json<LoginRequest>,
ip_addr: SocketAddr,
cookies: &CookieJar<'_>,
) -> Result<String, Custom<String>> {
let db = conn.into_inner();
println!("{} {}", login_form.application_id, login_form.password);
let session_token = CandidateService::login(
db,
login_form.application_id,
login_form.password.to_string(),
ip_addr.ip().to_string(),
)
.await;
if let Err(e) = session_token {
return Err(Custom(
Status::from_code(e.code()).unwrap_or(Status::InternalServerError),
e.to_string(),
));
} else {
let session_token = session_token.unwrap();
cookies.add_private(Cookie::new("id", session_token.clone()));
return Ok(session_token);
}
}
#[get("/whoami")]
pub async fn whoami(session: CandidateAuth) -> Result<String, Custom<String>> {
let candidate: entity::candidate::Model = session.into();
Ok(candidate.application.to_string())
}
#[put("/details", data = "<details>")]
pub async fn fill_details(
conn: Connection<'_, Db>,
details: Json<UserDetails>,
session: CandidateAuth,
) -> Result<String, Custom<String>> {
let db = conn.into_inner();
let form = details.into_inner();
let candidate: entity::candidate::Model = session.into();
let candidate = CandidateService::add_user_details(db, candidate, form).await;
if candidate.is_err() {
// TODO cleanup
let e = candidate.err().unwrap();
return Err(Custom(
Status::from_code(e.code()).unwrap_or_default(),
e.message(),
));
}
Ok("Details added".to_string())
}

View file

@ -0,0 +1,2 @@
pub mod admin;
pub mod candidate;