feat: support tmp token
This commit is contained in:
@@ -12,3 +12,4 @@ diesel = { version = "1.4.8", features= ["sqlite", "chrono"] }
|
||||
chrono = { version="0.4", features=["serde"] }
|
||||
rand = "0.8.5"
|
||||
dotenv = "0.15.0"
|
||||
sha2 = "0.10.2"
|
||||
|
||||
@@ -14,28 +14,46 @@ pub fn catch_401_error() -> Value {
|
||||
}
|
||||
|
||||
pub struct CurrentUser {
|
||||
id: Option<i32>, // tmp user has no id, only for block
|
||||
namehash: String,
|
||||
is_admin: bool,
|
||||
custom_title: String,
|
||||
}
|
||||
|
||||
#[rocket::async_trait]
|
||||
impl<'r> FromRequest<'r> for CurrentUser {
|
||||
type Error = ();
|
||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||
let rh = request.rocket().state::<RandomHasher>().unwrap();
|
||||
let mut cu: Option<CurrentUser> = None;
|
||||
|
||||
if let Some(token) = request.headers().get_one("User-Token") {
|
||||
let conn = establish_connection();
|
||||
if let Some(user) = User::get_by_token(&conn, token) {
|
||||
return request::Outcome::Success(CurrentUser {
|
||||
namehash: request
|
||||
.rocket()
|
||||
.state::<RandomHasher>()
|
||||
.unwrap()
|
||||
.hash_with_salt(&user.name),
|
||||
is_admin: user.is_admin,
|
||||
let sp = token.split('_').collect::<Vec<&str>>();
|
||||
if sp.len() == 2 && sp[0] == rh.get_tmp_token() {
|
||||
let namehash = rh.hash_with_salt(sp[1]);
|
||||
cu = Some(CurrentUser {
|
||||
id: None,
|
||||
custom_title: format!("TODO: {}", &namehash),
|
||||
namehash: namehash,
|
||||
is_admin: false,
|
||||
});
|
||||
} else {
|
||||
let conn = establish_connection();
|
||||
if let Some(user) = User::get_by_token(&conn, token) {
|
||||
let namehash = rh.hash_with_salt(&user.name);
|
||||
cu = Some(CurrentUser {
|
||||
id: Some(user.id),
|
||||
custom_title: format!("TODO: {}", &namehash),
|
||||
namehash: namehash,
|
||||
is_admin: user.is_admin,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
request::Outcome::Failure((Status::Unauthorized, ()))
|
||||
match cu {
|
||||
Some(u) => request::Outcome::Success(u),
|
||||
None => request::Outcome::Failure((Status::Unauthorized, ())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,6 +95,13 @@ impl<'r> Responder<'r, 'static> for APIError {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! look {
|
||||
($s:expr) => {
|
||||
format!("{}...{}", &$s[..2], &$s[$s.len() - 2..])
|
||||
};
|
||||
}
|
||||
|
||||
pub type API<T> = Result<T, APIError>;
|
||||
|
||||
pub mod post;
|
||||
pub mod systemlog;
|
||||
|
||||
15
src/api/systemlog.rs
Normal file
15
src/api/systemlog.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::api::{CurrentUser, API};
|
||||
use crate::random_hasher::RandomHasher;
|
||||
use chrono::SubsecRound;
|
||||
use rocket::serde::json::{json, Value};
|
||||
use rocket::State;
|
||||
|
||||
#[get("/systemlog")]
|
||||
pub fn get_systemlog(user: CurrentUser, rh: &State<RandomHasher>) -> API<Value> {
|
||||
Ok(json!({
|
||||
"tmp_token": rh.get_tmp_token(),
|
||||
"salt": look!(rh.salt),
|
||||
"start_time": rh.start_time.round_subsecs(0),
|
||||
"custom_title": user.custom_title,
|
||||
}))
|
||||
}
|
||||
24
src/main.rs
24
src/main.rs
@@ -4,10 +4,10 @@ extern crate rocket;
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
mod random_hasher;
|
||||
mod models;
|
||||
mod schema;
|
||||
mod api;
|
||||
mod models;
|
||||
mod random_hasher;
|
||||
mod schema;
|
||||
|
||||
use random_hasher::RandomHasher;
|
||||
|
||||
@@ -15,14 +15,16 @@ use random_hasher::RandomHasher;
|
||||
fn rocket() -> _ {
|
||||
load_env();
|
||||
rocket::build()
|
||||
.mount("/_api", routes![
|
||||
api::post::get_list,
|
||||
api::post::get_one,
|
||||
api::post::publish_post
|
||||
])
|
||||
.register("/_api", catchers![
|
||||
api::catch_401_error
|
||||
])
|
||||
.mount(
|
||||
"/_api/v1",
|
||||
routes![
|
||||
api::post::get_list,
|
||||
api::post::get_one,
|
||||
api::post::publish_post,
|
||||
api::systemlog::get_systemlog,
|
||||
],
|
||||
)
|
||||
.register("/_api", catchers![api::catch_401_error])
|
||||
.manage(RandomHasher::get_random_one())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use chrono::{offset::Local, DateTime};
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub struct RandomHasher {
|
||||
salt: String,
|
||||
pub salt: String,
|
||||
pub start_time: DateTime<Local>,
|
||||
}
|
||||
|
||||
impl RandomHasher {
|
||||
@@ -12,11 +15,23 @@ impl RandomHasher {
|
||||
.take(16)
|
||||
.map(char::from)
|
||||
.collect(),
|
||||
start_time: Local::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash_with_salt(&self, text: &str) -> String {
|
||||
// TODO
|
||||
format!("hash({}+{})", self.salt, text)
|
||||
let mut h = Sha256::new();
|
||||
h.update(text);
|
||||
h.update(&self.salt);
|
||||
format!("{:X}", h.finalize())[5..21].to_string()
|
||||
}
|
||||
|
||||
pub fn get_tmp_token(&self) -> String {
|
||||
// 每15分钟变化一次
|
||||
self.hash_with_salt(&format!(
|
||||
"{}_{}",
|
||||
Local::now().timestamp() / 60 / 15,
|
||||
self.start_time.timestamp_subsec_nanos()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user