Browse Source

feat: support tmp token

master
hole-thu 3 years ago
parent
commit
d9aec4d613
  1. 1
      Cargo.toml
  2. 39
      src/api/mod.rs
  3. 15
      src/api/systemlog.rs
  4. 18
      src/main.rs
  5. 21
      src/random_hasher.rs

1
Cargo.toml

@ -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"

39
src/api/mod.rs

@ -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 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(); let conn = establish_connection();
if let Some(user) = User::get_by_token(&conn, token) { if let Some(user) = User::get_by_token(&conn, token) {
return request::Outcome::Success(CurrentUser { let namehash = rh.hash_with_salt(&user.name);
namehash: request cu = Some(CurrentUser {
.rocket() id: Some(user.id),
.state::<RandomHasher>() custom_title: format!("TODO: {}", &namehash),
.unwrap() namehash: namehash,
.hash_with_salt(&user.name),
is_admin: user.is_admin, 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

@ -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,
}))
}

18
src/main.rs

@ -4,10 +4,10 @@ extern crate rocket;
#[macro_use] #[macro_use]
extern crate diesel; extern crate diesel;
mod random_hasher; mod api;
mod models; mod models;
mod random_hasher;
mod schema; mod schema;
mod api;
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/v1",
routes![
api::post::get_list, api::post::get_list,
api::post::get_one, api::post::get_one,
api::post::publish_post api::post::publish_post,
]) api::systemlog::get_systemlog,
.register("/_api", catchers![ ],
api::catch_401_error )
]) .register("/_api", catchers![api::catch_401_error])
.manage(RandomHasher::get_random_one()) .manage(RandomHasher::get_random_one())
} }

21
src/random_hasher.rs

@ -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()
))
} }
} }

Loading…
Cancel
Save