JSON web token protection for API routes
This commit is contained in:
parent
c19d3f125e
commit
e638075cd1
|
@ -1863,6 +1863,26 @@ dependencies = [
|
||||||
"unicode-xid 0.2.1",
|
"unicode-xid 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.24",
|
||||||
|
"quote 1.0.7",
|
||||||
|
"syn 1.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
|
@ -2259,6 +2279,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2 0.9.1",
|
"sha2 0.9.1",
|
||||||
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
|
|
@ -18,6 +18,7 @@ rocket = "0.4"
|
||||||
rocket_oauth2 = "0.4"
|
rocket_oauth2 = "0.4"
|
||||||
serde = { version = "^1", features = ["derive"] }
|
serde = { version = "^1", features = ["derive"] }
|
||||||
serde_json = "^1"
|
serde_json = "^1"
|
||||||
|
thiserror = "1"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = "0.2"
|
tracing-subscriber = "0.2"
|
||||||
tracing-log = "0.1"
|
tracing-log = "0.1"
|
||||||
|
|
92
src/api.rs
92
src/api.rs
|
@ -1,15 +1,89 @@
|
||||||
use crate::{schema, models, MainDatabase};
|
use crate::{jwt, models, schema, MainDatabase};
|
||||||
|
use color_eyre::eyre::Report;
|
||||||
use diesel::prelude::*;
|
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_contrib::{json::Json, uuid::Uuid};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
#[tracing::instrument(skip(conn))]
|
#[tracing::instrument]
|
||||||
#[get("/user/<uuid>")]
|
#[get("/user/<uuid>")]
|
||||||
pub fn get_user(conn: MainDatabase, uuid: Uuid) -> Json<models::User> {
|
pub fn get_user(user: models::User, uuid: Uuid) -> Result<Json<models::User>> {
|
||||||
use schema::users::dsl::users;
|
if uuid != user.id {
|
||||||
let result = users
|
return Err(Error::LackPermissions);
|
||||||
.find(uuid.into_inner())
|
}
|
||||||
.get_result::<models::User>(&*conn)
|
|
||||||
.expect("to find user");
|
|
||||||
|
|
||||||
Json(result)
|
Ok(Json(user))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("internal database error: {0}")]
|
||||||
|
Database(#[from] diesel::result::Error),
|
||||||
|
|
||||||
|
#[error("bad or no authorization")]
|
||||||
|
BadOrNoAuth,
|
||||||
|
|
||||||
|
#[error("you lack needed permissions")]
|
||||||
|
LackPermissions,
|
||||||
|
|
||||||
|
#[error("internal server error")]
|
||||||
|
InternalServerError(#[from] Report),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Responder<'a> for Error {
|
||||||
|
fn respond_to(self, _: &Request) -> ::std::result::Result<Response<'a>, Status> {
|
||||||
|
match self {
|
||||||
|
Error::Database(why) => Response::build()
|
||||||
|
.header(ContentType::Plain)
|
||||||
|
.status(Status::InternalServerError)
|
||||||
|
.sized_body(Cursor::new(format!("{}", why)))
|
||||||
|
.ok(),
|
||||||
|
Error::BadOrNoAuth | Error::LackPermissions => Response::build()
|
||||||
|
.header(ContentType::Plain)
|
||||||
|
.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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum AuthError {
|
||||||
|
BadCount,
|
||||||
|
Missing,
|
||||||
|
Invaild,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T = ()> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
|
impl<'a, 'r> FromRequest<'a, 'r> for models::User {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let keys: Vec<_> = request.headers().get("authorization").collect();
|
||||||
|
match keys.len() {
|
||||||
|
0 => Outcome::Failure((Status::BadRequest, ())),
|
||||||
|
1 => {
|
||||||
|
let tok = keys[0].to_string();
|
||||||
|
let conn = request.guard::<MainDatabase>()?;
|
||||||
|
match jwt::verify(tok, conn) {
|
||||||
|
Err(why) => {
|
||||||
|
tracing::error!("JWT verification error: {}", why);
|
||||||
|
Outcome::Failure((Status::Unauthorized, ()))
|
||||||
|
}
|
||||||
|
Ok(user) => Outcome::Success(user),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Outcome::Failure((Status::BadRequest, ())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,8 +47,8 @@ pub fn verify(token: String, conn: MainDatabase) -> Result<models::User> {
|
||||||
.find(uuid::Uuid::parse_str(&jti)?)
|
.find(uuid::Uuid::parse_str(&jti)?)
|
||||||
.get_result::<models::Token>(&*conn)?;
|
.get_result::<models::Token>(&*conn)?;
|
||||||
|
|
||||||
if tok.deleted_at.is_none() {
|
if tok.deleted_at.is_some() {
|
||||||
return Err(eyre!("token was deleted"));
|
return Err(eyre!("token was deleted at {}", tok.deleted_at.unwrap()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if tok.user_id != uid {
|
if tok.user_id != uid {
|
||||||
|
|
|
@ -119,7 +119,8 @@ fn main() -> Result<()> {
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
tracing::trace!("JWT secret: {:?}", *jwt::SECRET);
|
let _ = *jwt::SECRET;
|
||||||
|
|
||||||
rocket::ignite()
|
rocket::ignite()
|
||||||
.attach(OAuth2::<Gitea>::fairing("gitea"))
|
.attach(OAuth2::<Gitea>::fairing("gitea"))
|
||||||
.attach(MainDatabase::fairing())
|
.attach(MainDatabase::fairing())
|
||||||
|
|
Loading…
Reference in New Issue