token creation call

This commit is contained in:
Cadey Ratio 2020-10-28 15:00:57 -04:00
parent 392d9642b7
commit 550e80f91c
2 changed files with 76 additions and 53 deletions

View File

@ -20,13 +20,13 @@ pub fn get_user(user: models::User, uuid: Uuid) -> Result<Json<models::User>> {
Ok(Json(user)) Ok(Json(user))
} }
#[tracing::instrument] #[instrument]
#[get("/whoami")] #[get("/whoami")]
pub fn whoami(user: models::User) -> Json<models::User> { pub fn whoami(user: models::User) -> Json<models::User> {
Json(user) Json(user)
} }
#[tracing::instrument(skip(conn))] #[instrument(skip(conn))]
#[get("/token")] #[get("/token")]
pub fn get_tokens(user: models::User, conn: MainDatabase) -> Result<Json<Vec<models::Token>>> { pub fn get_tokens(user: models::User, conn: MainDatabase) -> Result<Json<Vec<models::Token>>> {
use schema::tokens::dsl::*; use schema::tokens::dsl::*;
@ -39,14 +39,16 @@ pub fn get_tokens(user: models::User, conn: MainDatabase) -> Result<Json<Vec<mod
)) ))
} }
#[tracing::instrument(skip(conn))] #[instrument(skip(conn))]
#[delete("/token/<uuid>")] #[delete("/token/<uuid>")]
pub fn delete_token(user: models::User, conn: MainDatabase, uuid: Uuid) -> Result { pub fn delete_token(user: models::User, conn: MainDatabase, uuid: Uuid) -> Result {
use schema::tokens::dsl::*; use schema::tokens::dsl::*;
let uuid = uuid.into_inner(); let uuid = uuid.into_inner();
let tok: models::Token = tokens.find(uuid.clone()) let tok: models::Token = tokens
.get_result(&*conn).map_err(Error::Database)?; .find(uuid.clone())
.get_result(&*conn)
.map_err(Error::Database)?;
if tok.user_id != user.id && !user.is_admin { if tok.user_id != user.id && !user.is_admin {
return Err(Error::LackPermissions); return Err(Error::LackPermissions);
@ -59,6 +61,20 @@ pub fn delete_token(user: models::User, conn: MainDatabase, uuid: Uuid) -> Resul
Ok(()) Ok(())
} }
#[instrument(skip(conn))]
#[post("/token")]
pub fn create_token(user: models::User, conn: MainDatabase) -> Result<String> {
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)] #[derive(thiserror::Error, Debug)]
pub enum Error { pub enum Error {
#[error("internal database error: {0}")] #[error("internal database error: {0}")]
@ -70,8 +86,11 @@ pub enum Error {
#[error("you lack needed permissions")] #[error("you lack needed permissions")]
LackPermissions, LackPermissions,
#[error("internal server error")] #[error("internal server error: {0}")]
InternalServerError(#[from] Report), InternalServerError(#[from] Report),
#[error("external dependency failed: {0}")]
ExternalDependencyFailed(Report),
} }
impl<'a> Responder<'a> for Error { impl<'a> Responder<'a> for Error {
@ -87,11 +106,13 @@ impl<'a> Responder<'a> for Error {
.status(Status::Unauthorized) .status(Status::Unauthorized)
.sized_body(Cursor::new(format!("{}", self))) .sized_body(Cursor::new(format!("{}", self)))
.ok(), .ok(),
Error::InternalServerError(why) => Response::build() Error::InternalServerError(why) | Error::ExternalDependencyFailed(why) => {
.header(ContentType::Plain) Response::build()
.status(Status::InternalServerError) .header(ContentType::Plain)
.sized_body(Cursor::new(format!("{}", why))) .status(Status::InternalServerError)
.ok(), .sized_body(Cursor::new(format!("{}", why)))
.ok()
}
} }
} }
} }

View File

@ -6,6 +6,8 @@ extern crate diesel;
extern crate rocket; extern crate rocket;
#[macro_use] #[macro_use]
extern crate rocket_contrib; extern crate rocket_contrib;
#[macro_use]
extern crate tracing;
use color_eyre::eyre::Result; use color_eyre::eyre::Result;
use diesel::pg::PgConnection; use diesel::pg::PgConnection;
@ -28,23 +30,24 @@ pub struct MainDatabase(PgConnection);
pub struct Gitea; pub struct Gitea;
#[tracing::instrument(skip(oauth2, cookies))] #[instrument(skip(oauth2, cookies))]
#[get("/login/gitea")] #[get("/login/gitea")]
fn gitea_login(oauth2: OAuth2<Gitea>, mut cookies: Cookies<'_>) -> Redirect { fn gitea_login(oauth2: OAuth2<Gitea>, mut cookies: Cookies<'_>) -> Redirect {
oauth2.get_redirect(&mut cookies, &[""]).unwrap() oauth2.get_redirect(&mut cookies, &[""]).unwrap()
} }
#[tracing::instrument(skip(conn, token, cookies))] #[instrument(skip(conn, token, cookies))]
#[get("/auth/gitea")] #[get("/auth/gitea")]
fn gitea_callback( fn gitea_callback(
conn: MainDatabase, conn: MainDatabase,
token: TokenResponse<Gitea>, token: TokenResponse<Gitea>,
mut cookies: Cookies<'_>, mut cookies: Cookies<'_>,
) -> String { ) -> api::Result<String> {
let tok = token.access_token().to_string(); let tok = token.access_token().to_string();
let refresh = token.refresh_token().unwrap().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::{ use schema::{
gitea_tokens, tokens, gitea_tokens, tokens,
@ -53,47 +56,43 @@ fn gitea_callback(
table as users_table, table as users_table,
}, },
}; };
let user: models::User = match users let u: Vec<models::User> = users
.filter(email.eq(gitea_user.email.clone())) .filter(email.eq(gitea_user.email.clone()))
.limit(1) .limit(1)
.load::<models::User>(&*conn) .load::<models::User>(&*conn)
{ .map_err(api::Error::Database)?;
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,
};
let u: models::User = diesel::insert_into(users_table) let user = if u.len() == 0 {
.values(&u) let u = models::NewUser {
.get_result(&*conn) salutation: gitea_user.full_name,
.expect("able to insert user"); email: gitea_user.email,
is_admin: gitea_user.is_admin,
is_locked: false,
tier: 0,
};
let tok = models::NewGiteaToken { let u: models::User = diesel::insert_into(users_table)
user_id: u.id.clone(), .values(&u)
access_token: tok, .get_result(&*conn)
refresh_token: refresh, .map_err(api::Error::Database)?;
};
let _: models::GiteaToken = diesel::insert_into(gitea_tokens::table) let tok = models::NewGiteaToken {
.values(&tok) user_id: u.id.clone(),
.get_result(&*conn) access_token: tok,
.expect("able to insert token"); refresh_token: refresh,
};
u let _: models::GiteaToken = diesel::insert_into(gitea_tokens::table)
} else { .values(&tok)
tracing::info!("{} {:?} logged in", u[0].id, u[0].salutation); .get_result(&*conn)
u[0].clone() .map_err(api::Error::Database)?;
}
} info!("new account created for {:?}", u);
Err(why) => {
tracing::error!("error reading from database: {}", why); u
todo!("error response") } else {
} info!("{} {:?} logged in", u[0].id, u[0].salutation);
u[0].clone()
}; };
let tok: models::Token = diesel::insert_into(tokens::table) let tok: models::Token = diesel::insert_into(tokens::table)
@ -102,23 +101,25 @@ fn gitea_callback(
}) })
.get_result(&*conn) .get_result(&*conn)
.expect("create token information"); .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"); let tok = jwt::make(user.id, tok.id).expect("to sign JWT");
// Set a private cookie with the access token
cookies.add_private( cookies.add_private(
Cookie::build("token", tok.clone()) Cookie::build("token", tok.clone())
.same_site(SameSite::Lax) .same_site(SameSite::Lax)
.finish(), .finish(),
); );
tok Ok(tok)
} }
fn main() -> Result<()> { fn main() -> Result<()> {
color_eyre::install()?; color_eyre::install()?;
tracing_subscriber::fmt::init(); 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; let _ = *jwt::SECRET;
rocket::ignite() rocket::ignite()
@ -131,7 +132,8 @@ fn main() -> Result<()> {
api::whoami, api::whoami,
api::get_user, api::get_user,
api::get_tokens, api::get_tokens,
api::delete_token api::delete_token,
api::create_token,
], ],
) )
.mount("/", routes![gitea_login, gitea_callback]) .mount("/", routes![gitea_login, gitea_callback])