split handler families into files
This commit is contained in:
parent
df2f2dcd46
commit
c99bc1119f
|
@ -2,10 +2,11 @@ CREATE TABLE IF NOT EXISTS handlers
|
|||
( id UUID DEFAULT uuid_generate_v4() NOT NULL
|
||||
, user_id UUID NOT NULL
|
||||
, human_name VARCHAR NOT NULL
|
||||
, current_version VARCHAR NOT NULL
|
||||
, async_impl BOOLEAN DEFAULT false
|
||||
, current_version VARCHAR
|
||||
, async_impl BOOLEAN NOT NULL DEFAULT false
|
||||
, created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
, updated_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
, deleted_at TIMESTAMP
|
||||
, PRIMARY KEY (id)
|
||||
, CONSTRAINT fk_user_id
|
||||
FOREIGN KEY (user_id)
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
use crate::{models, schema, MainDatabase};
|
||||
use super::{Error, Result};
|
||||
use chrono::prelude::*;
|
||||
use diesel::prelude::*;
|
||||
use rocket_contrib::{json::Json, uuid::Uuid};
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[get("/handler")]
|
||||
pub fn list(user: models::User, conn: MainDatabase) -> Result<Json<Vec<models::Handler>>> {
|
||||
use schema::handlers::dsl::*;
|
||||
|
||||
Ok(Json(
|
||||
handlers
|
||||
.filter(user_id.eq(user.id))
|
||||
.load::<models::Handler>(&*conn)
|
||||
.map_err(Error::Database)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[get("/handler/<uuid>")]
|
||||
pub fn get(
|
||||
user: models::User,
|
||||
uuid: Uuid,
|
||||
conn: MainDatabase,
|
||||
) -> Result<Json<models::Handler>> {
|
||||
use schema::handlers::dsl::*;
|
||||
let uuid = uuid.into_inner();
|
||||
let handler = handlers
|
||||
.find(uuid)
|
||||
.get_result::<models::Handler>(&*conn)
|
||||
.map_err(Error::Database)?;
|
||||
|
||||
if handler.user_id != user.id {
|
||||
Err(Error::LackPermissions)
|
||||
} else {
|
||||
Ok(Json(handler))
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[delete("/handler/<uuid>")]
|
||||
pub fn delete(user: models::User, uuid: Uuid, conn: MainDatabase) -> Result {
|
||||
use schema::handlers::dsl::*;
|
||||
let uuid = uuid.into_inner();
|
||||
|
||||
let hdl: models::Handler = handlers
|
||||
.find(uuid.clone())
|
||||
.get_result(&*conn)
|
||||
.map_err(Error::Database)?;
|
||||
|
||||
if hdl.user_id != user.id && !user.is_admin {
|
||||
return Err(Error::LackPermissions);
|
||||
}
|
||||
|
||||
diesel::update(handlers.find(uuid))
|
||||
.set(deleted_at.eq(Utc::now().naive_utc()))
|
||||
.get_result::<models::Handler>(&*conn)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,79 +1,16 @@
|
|||
use crate::{jwt, models, schema, MainDatabase};
|
||||
use chrono::prelude::*;
|
||||
use crate::{jwt, models, MainDatabase};
|
||||
use color_eyre::eyre::Report;
|
||||
use diesel::prelude::*;
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::request::{self, FromRequest, Request};
|
||||
use rocket::response::Responder;
|
||||
use rocket::Outcome;
|
||||
use rocket::Response;
|
||||
use rocket_contrib::{json::Json, uuid::Uuid};
|
||||
use rocket::{
|
||||
http::{ContentType, Status},
|
||||
request::{self, FromRequest, Request},
|
||||
response::Responder,
|
||||
Outcome, Response,
|
||||
};
|
||||
use std::io::Cursor;
|
||||
|
||||
#[tracing::instrument]
|
||||
#[get("/user/<uuid>")]
|
||||
pub fn get_user(user: models::User, uuid: Uuid) -> Result<Json<models::User>> {
|
||||
if uuid != user.id {
|
||||
return Err(Error::LackPermissions);
|
||||
}
|
||||
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
#[get("/whoami")]
|
||||
pub fn whoami(user: models::User) -> Json<models::User> {
|
||||
Json(user)
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[get("/token")]
|
||||
pub fn get_tokens(user: models::User, conn: MainDatabase) -> Result<Json<Vec<models::Token>>> {
|
||||
use schema::tokens::dsl::*;
|
||||
|
||||
Ok(Json(
|
||||
tokens
|
||||
.filter(user_id.eq(user.id))
|
||||
.load::<models::Token>(&*conn)
|
||||
.map_err(Error::Database)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[delete("/token/<uuid>")]
|
||||
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)?;
|
||||
|
||||
if tok.user_id != user.id && !user.is_admin {
|
||||
return Err(Error::LackPermissions);
|
||||
}
|
||||
|
||||
diesel::update(tokens.find(uuid))
|
||||
.set(deleted_at.eq(Utc::now().naive_utc()))
|
||||
.get_result::<models::Token>(&*conn)?;
|
||||
|
||||
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)?)
|
||||
}
|
||||
pub mod handler;
|
||||
pub mod token;
|
||||
pub mod user;
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
|
@ -143,7 +80,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for models::User {
|
|||
|
||||
match jwt::verify(tok, conn) {
|
||||
Err(why) => {
|
||||
tracing::error!("JWT verification error: {}", why);
|
||||
error!("JWT verification error: {}", why);
|
||||
Outcome::Failure((Status::Unauthorized, ()))
|
||||
}
|
||||
Ok(user) => Outcome::Success(user),
|
||||
|
@ -155,7 +92,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for models::User {
|
|||
let tok = keys[0].to_string();
|
||||
match jwt::verify(tok, conn) {
|
||||
Err(why) => {
|
||||
tracing::error!("JWT verification error: {}", why);
|
||||
error!("JWT verification error: {}", why);
|
||||
Outcome::Failure((Status::Unauthorized, ()))
|
||||
}
|
||||
Ok(user) => Outcome::Success(user),
|
|
@ -0,0 +1,55 @@
|
|||
use crate::{jwt, models, schema, MainDatabase};
|
||||
use super::{Error, Result};
|
||||
use chrono::prelude::*;
|
||||
use diesel::prelude::*;
|
||||
use rocket_contrib::{json::Json, uuid::Uuid};
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[get("/token")]
|
||||
pub fn list(user: models::User, conn: MainDatabase) -> Result<Json<Vec<models::Token>>> {
|
||||
use schema::tokens::dsl::*;
|
||||
|
||||
Ok(Json(
|
||||
tokens
|
||||
.filter(user_id.eq(user.id))
|
||||
.load::<models::Token>(&*conn)
|
||||
.map_err(Error::Database)?,
|
||||
))
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[delete("/token/<uuid>")]
|
||||
pub fn delete(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)?;
|
||||
|
||||
if tok.user_id != user.id && !user.is_admin {
|
||||
return Err(Error::LackPermissions);
|
||||
}
|
||||
|
||||
diesel::update(tokens.find(uuid))
|
||||
.set(deleted_at.eq(Utc::now().naive_utc()))
|
||||
.get_result::<models::Token>(&*conn)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(conn))]
|
||||
#[post("/token")]
|
||||
pub fn create(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)?)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
use crate::models;
|
||||
use super::{Error, Result};
|
||||
use rocket_contrib::{json::Json, uuid::Uuid};
|
||||
|
||||
#[instrument]
|
||||
#[get("/user/<uuid>")]
|
||||
pub fn get(user: models::User, uuid: Uuid) -> Result<Json<models::User>> {
|
||||
if uuid != user.id {
|
||||
return Err(Error::LackPermissions);
|
||||
}
|
||||
|
||||
Ok(Json(user))
|
||||
}
|
||||
|
||||
#[instrument]
|
||||
#[get("/whoami")]
|
||||
pub fn whoami(user: models::User) -> Json<models::User> {
|
||||
Json(user)
|
||||
}
|
91
src/gitea.rs
91
src/gitea.rs
|
@ -1,4 +1,11 @@
|
|||
use crate::{MainDatabase, Gitea, models, jwt, schema, api};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use diesel::prelude::*;
|
||||
use rocket::{
|
||||
http::{Cookie, Cookies, SameSite},
|
||||
response::Redirect,
|
||||
};
|
||||
use rocket_oauth2::{OAuth2, TokenResponse};
|
||||
|
||||
/// A user.
|
||||
/// https://try.gitea.io/api/swagger#model-User
|
||||
|
@ -25,3 +32,87 @@ pub fn user(token: String) -> std::io::Result<User> {
|
|||
let user: User = resp.into_json_deserialize()?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
#[instrument(skip(oauth2, cookies))]
|
||||
#[get("/login/gitea")]
|
||||
pub fn login(oauth2: OAuth2<Gitea>, mut cookies: Cookies<'_>) -> Redirect {
|
||||
oauth2.get_redirect(&mut cookies, &[""]).unwrap()
|
||||
}
|
||||
|
||||
#[instrument(skip(conn, token, cookies))]
|
||||
#[get("/auth/gitea")]
|
||||
pub fn callback(
|
||||
conn: MainDatabase,
|
||||
token: TokenResponse<Gitea>,
|
||||
mut cookies: Cookies<'_>,
|
||||
) -> api::Result<String> {
|
||||
let tok = token.access_token().to_string();
|
||||
let refresh = token.refresh_token().unwrap().to_string();
|
||||
|
||||
let gitea_user =
|
||||
user(tok.clone()).map_err(|why| api::Error::ExternalDependencyFailed(why.into()))?;
|
||||
|
||||
use schema::{
|
||||
gitea_tokens, tokens,
|
||||
users::{
|
||||
dsl::{email, users},
|
||||
table as users_table,
|
||||
},
|
||||
};
|
||||
let u: Vec<models::User> = users
|
||||
.filter(email.eq(gitea_user.email.clone()))
|
||||
.limit(1)
|
||||
.load::<models::User>(&*conn)
|
||||
.map_err(api::Error::Database)?;
|
||||
|
||||
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 u: models::User = diesel::insert_into(users_table)
|
||||
.values(&u)
|
||||
.get_result(&*conn)
|
||||
.map_err(api::Error::Database)?;
|
||||
|
||||
let tok = models::NewGiteaToken {
|
||||
user_id: u.id.clone(),
|
||||
access_token: tok,
|
||||
refresh_token: refresh,
|
||||
};
|
||||
|
||||
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)
|
||||
.values(&models::NewToken {
|
||||
user_id: user.id.clone(),
|
||||
})
|
||||
.get_result(&*conn)
|
||||
.map_err(api::Error::Database)?;
|
||||
info!("created new token for {} with id {}", user.id, tok.id);
|
||||
|
||||
let tok = jwt::make(user.id, tok.id).map_err(api::Error::InternalServerError)?;
|
||||
|
||||
cookies.add_private(
|
||||
Cookie::build("token", tok.clone())
|
||||
.same_site(SameSite::Lax)
|
||||
.finish(),
|
||||
);
|
||||
|
||||
Ok(tok)
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ lazy_static! {
|
|||
.to_string();
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
#[instrument]
|
||||
pub fn make(user_id: uuid::Uuid, token_id: uuid::Uuid) -> Result<String> {
|
||||
let key: Hmac<Sha256> = Hmac::new_varkey(&*SECRET.as_bytes()).unwrap();
|
||||
let mut claims = BTreeMap::new();
|
||||
|
@ -27,7 +27,7 @@ pub fn make(user_id: uuid::Uuid, token_id: uuid::Uuid) -> Result<String> {
|
|||
Ok(token_str)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(token, conn))]
|
||||
#[instrument(skip(token, conn))]
|
||||
pub fn verify(token: String, conn: MainDatabase) -> Result<models::User> {
|
||||
use schema::{tokens::dsl::tokens, users::dsl::users};
|
||||
let key: Hmac<Sha256> = Hmac::new_varkey(&*SECRET.as_bytes()).unwrap();
|
||||
|
|
106
src/main.rs
106
src/main.rs
|
@ -11,13 +11,8 @@ extern crate tracing;
|
|||
|
||||
use color_eyre::eyre::Result;
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::prelude::*;
|
||||
use rocket::{
|
||||
http::{Cookie, Cookies, SameSite},
|
||||
response::Redirect,
|
||||
};
|
||||
use rocket_contrib::helmet::SpaceHelmet;
|
||||
use rocket_oauth2::{OAuth2, TokenResponse};
|
||||
use rocket_oauth2::{OAuth2};
|
||||
|
||||
pub mod api;
|
||||
pub mod gitea;
|
||||
|
@ -30,90 +25,6 @@ pub struct MainDatabase(PgConnection);
|
|||
|
||||
pub struct Gitea;
|
||||
|
||||
#[instrument(skip(oauth2, cookies))]
|
||||
#[get("/login/gitea")]
|
||||
fn gitea_login(oauth2: OAuth2<Gitea>, mut cookies: Cookies<'_>) -> Redirect {
|
||||
oauth2.get_redirect(&mut cookies, &[""]).unwrap()
|
||||
}
|
||||
|
||||
#[instrument(skip(conn, token, cookies))]
|
||||
#[get("/auth/gitea")]
|
||||
fn gitea_callback(
|
||||
conn: MainDatabase,
|
||||
token: TokenResponse<Gitea>,
|
||||
mut cookies: Cookies<'_>,
|
||||
) -> api::Result<String> {
|
||||
let tok = token.access_token().to_string();
|
||||
let refresh = token.refresh_token().unwrap().to_string();
|
||||
|
||||
let gitea_user =
|
||||
gitea::user(tok.clone()).map_err(|why| api::Error::ExternalDependencyFailed(why.into()))?;
|
||||
|
||||
use schema::{
|
||||
gitea_tokens, tokens,
|
||||
users::{
|
||||
dsl::{email, users},
|
||||
table as users_table,
|
||||
},
|
||||
};
|
||||
let u: Vec<models::User> = users
|
||||
.filter(email.eq(gitea_user.email.clone()))
|
||||
.limit(1)
|
||||
.load::<models::User>(&*conn)
|
||||
.map_err(api::Error::Database)?;
|
||||
|
||||
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 u: models::User = diesel::insert_into(users_table)
|
||||
.values(&u)
|
||||
.get_result(&*conn)
|
||||
.map_err(api::Error::Database)?;
|
||||
|
||||
let tok = models::NewGiteaToken {
|
||||
user_id: u.id.clone(),
|
||||
access_token: tok,
|
||||
refresh_token: refresh,
|
||||
};
|
||||
|
||||
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)
|
||||
.values(&models::NewToken {
|
||||
user_id: user.id.clone(),
|
||||
})
|
||||
.get_result(&*conn)
|
||||
.map_err(api::Error::Database)?;
|
||||
info!("created new token for {} with id {}", user.id, tok.id);
|
||||
|
||||
let tok = jwt::make(user.id, tok.id).map_err(api::Error::InternalServerError)?;
|
||||
|
||||
cookies.add_private(
|
||||
Cookie::build("token", tok.clone())
|
||||
.same_site(SameSite::Lax)
|
||||
.finish(),
|
||||
);
|
||||
|
||||
Ok(tok)
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
tracing_subscriber::fmt::init();
|
||||
|
@ -129,14 +40,17 @@ fn main() -> Result<()> {
|
|||
.mount(
|
||||
"/api",
|
||||
routes![
|
||||
api::whoami,
|
||||
api::get_user,
|
||||
api::get_tokens,
|
||||
api::delete_token,
|
||||
api::create_token,
|
||||
api::handler::list,
|
||||
api::handler::get,
|
||||
api::handler::delete,
|
||||
api::user::whoami,
|
||||
api::user::get,
|
||||
api::token::list,
|
||||
api::token::delete,
|
||||
api::token::create,
|
||||
],
|
||||
)
|
||||
.mount("/", routes![gitea_login, gitea_callback])
|
||||
.mount("/", routes![gitea::login, gitea::callback])
|
||||
.launch();
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::schema::{gitea_tokens, tokens, users};
|
||||
use crate::schema::{gitea_tokens, tokens, users, handlers};
|
||||
use chrono::NaiveDateTime;
|
||||
use serde::Serialize;
|
||||
use uuid::Uuid;
|
||||
|
@ -57,3 +57,24 @@ pub struct Token {
|
|||
pub updated_at: NaiveDateTime,
|
||||
pub deleted_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "handlers"]
|
||||
pub struct NewHandler {
|
||||
pub user_id: Uuid,
|
||||
pub human_name: String,
|
||||
pub current_version: Option<String>,
|
||||
pub async_impl: bool,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Debug, Clone, Serialize)]
|
||||
pub struct Handler {
|
||||
pub id: Uuid,
|
||||
pub user_id: Uuid,
|
||||
pub human_name: String,
|
||||
pub current_version: Option<String>,
|
||||
pub async_impl: bool,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub deleted_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
|
|
@ -14,10 +14,11 @@ table! {
|
|||
id -> Uuid,
|
||||
user_id -> Uuid,
|
||||
human_name -> Varchar,
|
||||
current_version -> Varchar,
|
||||
async_impl -> Nullable<Bool>,
|
||||
current_version -> Nullable<Varchar>,
|
||||
async_impl -> Bool,
|
||||
created_at -> Timestamp,
|
||||
updated_at -> Timestamp,
|
||||
deleted_at -> Nullable<Timestamp>,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue