From 550e80f91cc4ce6ba5407235b766798816edda66 Mon Sep 17 00:00:00 2001 From: Christine Dodrill Date: Wed, 28 Oct 2020 15:00:57 -0400 Subject: [PATCH] token creation call --- src/api.rs | 43 ++++++++++++++++++++------- src/main.rs | 86 +++++++++++++++++++++++++++-------------------------- 2 files changed, 76 insertions(+), 53 deletions(-) diff --git a/src/api.rs b/src/api.rs index af0f03d..64a707e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -20,13 +20,13 @@ pub fn get_user(user: models::User, uuid: Uuid) -> Result> { Ok(Json(user)) } -#[tracing::instrument] +#[instrument] #[get("/whoami")] pub fn whoami(user: models::User) -> Json { Json(user) } -#[tracing::instrument(skip(conn))] +#[instrument(skip(conn))] #[get("/token")] pub fn get_tokens(user: models::User, conn: MainDatabase) -> Result>> { use schema::tokens::dsl::*; @@ -39,14 +39,16 @@ pub fn get_tokens(user: models::User, conn: MainDatabase) -> Result")] pub fn delete_token(user: models::User, conn: MainDatabase, uuid: Uuid) -> Result { use schema::tokens::dsl::*; let uuid = uuid.into_inner(); - let tok: models::Token = tokens.find(uuid.clone()) - .get_result(&*conn).map_err(Error::Database)?; + let tok: models::Token = tokens + .find(uuid.clone()) + .get_result(&*conn) + .map_err(Error::Database)?; if tok.user_id != user.id && !user.is_admin { return Err(Error::LackPermissions); @@ -59,6 +61,20 @@ pub fn delete_token(user: models::User, conn: MainDatabase, uuid: Uuid) -> Resul Ok(()) } +#[instrument(skip(conn))] +#[post("/token")] +pub fn create_token(user: models::User, conn: MainDatabase) -> Result { + use schema::tokens; + + let tok: models::Token = diesel::insert_into(tokens::table) + .values(&models::NewToken { + user_id: user.id.clone(), + }) + .get_result(&*conn).map_err(Error::Database)?; + + Ok(jwt::make(user.id, tok.id)?) +} + #[derive(thiserror::Error, Debug)] pub enum Error { #[error("internal database error: {0}")] @@ -70,8 +86,11 @@ pub enum Error { #[error("you lack needed permissions")] LackPermissions, - #[error("internal server error")] + #[error("internal server error: {0}")] InternalServerError(#[from] Report), + + #[error("external dependency failed: {0}")] + ExternalDependencyFailed(Report), } impl<'a> Responder<'a> for Error { @@ -87,11 +106,13 @@ impl<'a> Responder<'a> for Error { .status(Status::Unauthorized) .sized_body(Cursor::new(format!("{}", self))) .ok(), - Error::InternalServerError(why) => Response::build() - .header(ContentType::Plain) - .status(Status::InternalServerError) - .sized_body(Cursor::new(format!("{}", why))) - .ok(), + Error::InternalServerError(why) | Error::ExternalDependencyFailed(why) => { + Response::build() + .header(ContentType::Plain) + .status(Status::InternalServerError) + .sized_body(Cursor::new(format!("{}", why))) + .ok() + } } } } diff --git a/src/main.rs b/src/main.rs index 74fb17f..54c4a77 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,8 @@ extern crate diesel; extern crate rocket; #[macro_use] extern crate rocket_contrib; +#[macro_use] +extern crate tracing; use color_eyre::eyre::Result; use diesel::pg::PgConnection; @@ -28,23 +30,24 @@ pub struct MainDatabase(PgConnection); pub struct Gitea; -#[tracing::instrument(skip(oauth2, cookies))] +#[instrument(skip(oauth2, cookies))] #[get("/login/gitea")] fn gitea_login(oauth2: OAuth2, mut cookies: Cookies<'_>) -> Redirect { oauth2.get_redirect(&mut cookies, &[""]).unwrap() } -#[tracing::instrument(skip(conn, token, cookies))] +#[instrument(skip(conn, token, cookies))] #[get("/auth/gitea")] fn gitea_callback( conn: MainDatabase, token: TokenResponse, mut cookies: Cookies<'_>, -) -> String { +) -> api::Result { let tok = token.access_token().to_string(); let refresh = token.refresh_token().unwrap().to_string(); - let gitea_user = gitea::user(tok.clone()).expect("gitea api call to work"); + let gitea_user = + gitea::user(tok.clone()).map_err(|why| api::Error::ExternalDependencyFailed(why.into()))?; use schema::{ gitea_tokens, tokens, @@ -53,47 +56,43 @@ fn gitea_callback( table as users_table, }, }; - let user: models::User = match users + let u: Vec = users .filter(email.eq(gitea_user.email.clone())) .limit(1) .load::(&*conn) - { - Ok(u) => { - if u.len() == 0 { - let u = models::NewUser { - salutation: gitea_user.full_name, - email: gitea_user.email, - is_admin: gitea_user.is_admin, - is_locked: false, - tier: 0, - }; + .map_err(api::Error::Database)?; - let u: models::User = diesel::insert_into(users_table) - .values(&u) - .get_result(&*conn) - .expect("able to insert user"); + let user = if u.len() == 0 { + let u = models::NewUser { + salutation: gitea_user.full_name, + email: gitea_user.email, + is_admin: gitea_user.is_admin, + is_locked: false, + tier: 0, + }; - let tok = models::NewGiteaToken { - user_id: u.id.clone(), - access_token: tok, - refresh_token: refresh, - }; + let u: models::User = diesel::insert_into(users_table) + .values(&u) + .get_result(&*conn) + .map_err(api::Error::Database)?; - let _: models::GiteaToken = diesel::insert_into(gitea_tokens::table) - .values(&tok) - .get_result(&*conn) - .expect("able to insert token"); + let tok = models::NewGiteaToken { + user_id: u.id.clone(), + access_token: tok, + refresh_token: refresh, + }; - u - } else { - tracing::info!("{} {:?} logged in", u[0].id, u[0].salutation); - u[0].clone() - } - } - Err(why) => { - tracing::error!("error reading from database: {}", why); - todo!("error response") - } + let _: models::GiteaToken = diesel::insert_into(gitea_tokens::table) + .values(&tok) + .get_result(&*conn) + .map_err(api::Error::Database)?; + + info!("new account created for {:?}", u); + + u + } else { + info!("{} {:?} logged in", u[0].id, u[0].salutation); + u[0].clone() }; let tok: models::Token = diesel::insert_into(tokens::table) @@ -102,23 +101,25 @@ fn gitea_callback( }) .get_result(&*conn) .expect("create token information"); - tracing::info!("created new token for {} with id {}", user.id, tok.id); + info!("created new token for {} with id {}", user.id, tok.id); let tok = jwt::make(user.id, tok.id).expect("to sign JWT"); - // Set a private cookie with the access token + cookies.add_private( Cookie::build("token", tok.clone()) .same_site(SameSite::Lax) .finish(), ); - tok + Ok(tok) } fn main() -> Result<()> { color_eyre::install()?; tracing_subscriber::fmt::init(); + // XXX(Xe): This looks ineffectual, however it forces jwt::SECRET to be + // evaluated and will kill the program if JWT_SECRET is not found. let _ = *jwt::SECRET; rocket::ignite() @@ -131,7 +132,8 @@ fn main() -> Result<()> { api::whoami, api::get_user, api::get_tokens, - api::delete_token + api::delete_token, + api::create_token, ], ) .mount("/", routes![gitea_login, gitea_callback])