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"] }
|
chrono = { version="0.4", features=["serde"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
sha2 = "0.10.2"
|
||||||
|
|||||||
@@ -14,28 +14,46 @@ pub fn catch_401_error() -> Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CurrentUser {
|
pub struct CurrentUser {
|
||||||
|
id: Option<i32>, // tmp user has no id, only for block
|
||||||
namehash: String,
|
namehash: String,
|
||||||
is_admin: bool,
|
is_admin: bool,
|
||||||
|
custom_title: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for CurrentUser {
|
impl<'r> FromRequest<'r> for CurrentUser {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
async fn from_request(request: &'r Request<'_>) -> request::Outcome<Self, Self::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") {
|
if let Some(token) = request.headers().get_one("User-Token") {
|
||||||
let conn = establish_connection();
|
let sp = token.split('_').collect::<Vec<&str>>();
|
||||||
if let Some(user) = User::get_by_token(&conn, token) {
|
if sp.len() == 2 && sp[0] == rh.get_tmp_token() {
|
||||||
return request::Outcome::Success(CurrentUser {
|
let namehash = rh.hash_with_salt(sp[1]);
|
||||||
namehash: request
|
cu = Some(CurrentUser {
|
||||||
.rocket()
|
id: None,
|
||||||
.state::<RandomHasher>()
|
custom_title: format!("TODO: {}", &namehash),
|
||||||
.unwrap()
|
namehash: namehash,
|
||||||
.hash_with_salt(&user.name),
|
is_admin: false,
|
||||||
is_admin: user.is_admin,
|
|
||||||
});
|
});
|
||||||
|
} 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 type API<T> = Result<T, APIError>;
|
||||||
|
|
||||||
pub mod post;
|
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]
|
#[macro_use]
|
||||||
extern crate diesel;
|
extern crate diesel;
|
||||||
|
|
||||||
mod random_hasher;
|
|
||||||
mod models;
|
|
||||||
mod schema;
|
|
||||||
mod api;
|
mod api;
|
||||||
|
mod models;
|
||||||
|
mod random_hasher;
|
||||||
|
mod schema;
|
||||||
|
|
||||||
use random_hasher::RandomHasher;
|
use random_hasher::RandomHasher;
|
||||||
|
|
||||||
@@ -15,14 +15,16 @@ use random_hasher::RandomHasher;
|
|||||||
fn rocket() -> _ {
|
fn rocket() -> _ {
|
||||||
load_env();
|
load_env();
|
||||||
rocket::build()
|
rocket::build()
|
||||||
.mount("/_api", routes![
|
.mount(
|
||||||
api::post::get_list,
|
"/_api/v1",
|
||||||
api::post::get_one,
|
routes![
|
||||||
api::post::publish_post
|
api::post::get_list,
|
||||||
])
|
api::post::get_one,
|
||||||
.register("/_api", catchers![
|
api::post::publish_post,
|
||||||
api::catch_401_error
|
api::systemlog::get_systemlog,
|
||||||
])
|
],
|
||||||
|
)
|
||||||
|
.register("/_api", catchers![api::catch_401_error])
|
||||||
.manage(RandomHasher::get_random_one())
|
.manage(RandomHasher::get_random_one())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
use chrono::{offset::Local, DateTime};
|
||||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
|
||||||
pub struct RandomHasher {
|
pub struct RandomHasher {
|
||||||
salt: String,
|
pub salt: String,
|
||||||
|
pub start_time: DateTime<Local>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RandomHasher {
|
impl RandomHasher {
|
||||||
@@ -12,11 +15,23 @@ impl RandomHasher {
|
|||||||
.take(16)
|
.take(16)
|
||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect(),
|
.collect(),
|
||||||
|
start_time: Local::now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_with_salt(&self, text: &str) -> String {
|
pub fn hash_with_salt(&self, text: &str) -> String {
|
||||||
// TODO
|
let mut h = Sha256::new();
|
||||||
format!("hash({}+{})", self.salt, text)
|
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