refactor: use i64 as user id
This commit is contained in:
parent
277b54711c
commit
0fa6536c7a
|
|
@ -461,6 +461,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde-aux",
|
"serde-aux",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"snowflake",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"token",
|
"token",
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,9 @@ tracing-actix-web = "0.7"
|
||||||
tracing-log = "0.1.1"
|
tracing-log = "0.1.1"
|
||||||
sqlx = { version = "0.6", default-features = false, features = ["runtime-actix-rustls", "macros", "postgres", "uuid", "chrono", "migrate", "offline"] }
|
sqlx = { version = "0.6", default-features = false, features = ["runtime-actix-rustls", "macros", "postgres", "uuid", "chrono", "migrate", "offline"] }
|
||||||
|
|
||||||
|
#Local crate
|
||||||
token = { path = "./crates/token" }
|
token = { path = "./crates/token" }
|
||||||
|
snowflake = { path = "./crates/snowflake" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
once_cell = "1.7.2"
|
once_cell = "1.7.2"
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ impl RevDB {
|
||||||
Ok(value.map(|value| value.to_vec()))
|
Ok(value.map(|value| value.to_vec()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optimize your data layout: Sled's B-Tree implementation works best when the keys are sequential,
|
// Optimize your data layout: Sled's B-Tree implementation works best when the keys are sequential,
|
||||||
// so try to organize the data in a way that maximizes sequential access.
|
// so try to organize the data in a way that maximizes sequential access.
|
||||||
fn make_seq_key(uid: i64, rev_id: i64) -> [u8; 16] {
|
fn make_seq_key(uid: i64, rev_id: i64) -> [u8; 16] {
|
||||||
|
|
@ -32,34 +33,3 @@ fn make_seq_key(uid: i64, rev_id: i64) -> [u8; 16] {
|
||||||
key[8..16].copy_from_slice(&rev_id.to_be_bytes());
|
key[8..16].copy_from_slice(&rev_id.to_be_bytes());
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::db::RevDB;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn insert_speed() {
|
|
||||||
let path = Path::new(".");
|
|
||||||
let db = RevDB::open(path).unwrap();
|
|
||||||
let start_time = Instant::now();
|
|
||||||
|
|
||||||
for i in 0..=100000 {
|
|
||||||
db.insert(1, i, b"hello world").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in 0..=100000 {
|
|
||||||
db.get(1, i).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let end_time = Instant::now();
|
|
||||||
let elapsed_time = end_time - start_time;
|
|
||||||
// Print the elapsed time in seconds and milliseconds
|
|
||||||
println!(
|
|
||||||
"Elapsed time: {}s, {}ms",
|
|
||||||
elapsed_time.as_secs(),
|
|
||||||
elapsed_time.subsec_millis()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::SystemTime;
|
||||||
|
|
||||||
const EPOCH: u64 = 1420070400000;
|
const EPOCH: u64 = 1637806706000;
|
||||||
const NODE_ID_BITS: u64 = 10;
|
const NODE_ID_BITS: u64 = 10;
|
||||||
const SEQUENCE_BITS: u64 = 12;
|
const SEQUENCE_BITS: u64 = 12;
|
||||||
const NODE_ID_SHIFT: u64 = SEQUENCE_BITS;
|
const NODE_ID_SHIFT: u64 = SEQUENCE_BITS;
|
||||||
const TIMESTAMP_SHIFT: u64 = NODE_ID_BITS + SEQUENCE_BITS;
|
const TIMESTAMP_SHIFT: u64 = NODE_ID_BITS + SEQUENCE_BITS;
|
||||||
const SEQUENCE_MASK: u64 = (1 << SEQUENCE_BITS) - 1;
|
const SEQUENCE_MASK: u64 = (1 << SEQUENCE_BITS) - 1;
|
||||||
const MAX_NODE_ID: u64 = (1 << NODE_ID_BITS) - 1;
|
|
||||||
|
|
||||||
struct Snowflake {
|
pub struct Snowflake {
|
||||||
node_id: u64,
|
node_id: u64,
|
||||||
sequence: u64,
|
sequence: u64,
|
||||||
last_timestamp: u64,
|
last_timestamp: u64,
|
||||||
|
|
@ -23,7 +22,7 @@ impl Snowflake {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_id(&mut self) -> u64 {
|
pub fn next_id(&mut self) -> i64 {
|
||||||
let timestamp = self.timestamp();
|
let timestamp = self.timestamp();
|
||||||
if timestamp < self.last_timestamp {
|
if timestamp < self.last_timestamp {
|
||||||
panic!("Clock moved backwards!");
|
panic!("Clock moved backwards!");
|
||||||
|
|
@ -39,7 +38,9 @@ impl Snowflake {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.last_timestamp = timestamp;
|
self.last_timestamp = timestamp;
|
||||||
(timestamp - EPOCH) << TIMESTAMP_SHIFT | self.node_id << NODE_ID_SHIFT | self.sequence
|
let id =
|
||||||
|
(timestamp - EPOCH) << TIMESTAMP_SHIFT | self.node_id << NODE_ID_SHIFT | self.sequence;
|
||||||
|
id as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_next_millis(&self) {
|
fn wait_next_millis(&self) {
|
||||||
|
|
@ -52,12 +53,21 @@ impl Snowflake {
|
||||||
fn timestamp(&self) -> u64 {
|
fn timestamp(&self) -> u64 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
.duration_since(SystemTime::UNIX_EPOCH)
|
.duration_since(SystemTime::UNIX_EPOCH)
|
||||||
.unwrap()
|
.expect("Clock moved backwards!")
|
||||||
.as_millis() as u64
|
.as_millis() as u64
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
#[cfg(test)]
|
||||||
let mut snowflake = Snowflake::new(1);
|
mod tests {
|
||||||
println!("{}", snowflake.next_id());
|
use crate::Snowflake;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gen_id() {
|
||||||
|
let mut snow_flake = Snowflake::new(1);
|
||||||
|
let id_1 = snow_flake.next_id();
|
||||||
|
let id_2 = snow_flake.next_id();
|
||||||
|
|
||||||
|
assert_ne!(id_1, id_2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
CREATE TABLE users (
|
CREATE TABLE users
|
||||||
uid uuid PRIMARY KEY,
|
(
|
||||||
|
uid bigint PRIMARY KEY,
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
email TEXT NOT NULL UNIQUE,
|
email TEXT NOT NULL UNIQUE,
|
||||||
|
|
|
||||||
|
|
@ -30,14 +30,7 @@ async fn login_handler(
|
||||||
let password = UserPassword::parse(req.password)
|
let password = UserPassword::parse(req.password)
|
||||||
.map_err(|_| InputParamsError::InvalidPassword)?
|
.map_err(|_| InputParamsError::InvalidPassword)?
|
||||||
.0;
|
.0;
|
||||||
let (resp, token) = login(
|
let (resp, token) = login(email, password, &state).await?;
|
||||||
state.pg_pool.clone(),
|
|
||||||
state.user.clone(),
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
&state.config.application.server_key,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Renews the session key, assigning existing session state to new key.
|
// Renews the session key, assigning existing session state to new key.
|
||||||
session.renew();
|
session.renew();
|
||||||
|
|
@ -68,16 +61,7 @@ async fn register_handler(req: Json<RegisterRequest>, state: Data<State>) -> Res
|
||||||
.map_err(|_| InputParamsError::InvalidPassword)?
|
.map_err(|_| InputParamsError::InvalidPassword)?
|
||||||
.0;
|
.0;
|
||||||
|
|
||||||
let resp = register(
|
let resp = register(name, email, password, &state).await?;
|
||||||
state.pg_pool.clone(),
|
|
||||||
state.user.clone(),
|
|
||||||
name,
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
&state.config.application.server_key,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(resp))
|
Ok(HttpResponse::Ok().json(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,11 @@ use actix_web::{dev::Server, web, web::Data, App, HttpServer};
|
||||||
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
use openssl::ssl::{SslAcceptor, SslAcceptorBuilder, SslFiletype, SslMethod};
|
||||||
use openssl::x509::X509;
|
use openssl::x509::X509;
|
||||||
use secrecy::{ExposeSecret, Secret};
|
use secrecy::{ExposeSecret, Secret};
|
||||||
|
use snowflake::Snowflake;
|
||||||
use sqlx::{postgres::PgPoolOptions, PgPool};
|
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||||
use std::net::TcpListener;
|
use std::net::TcpListener;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
|
|
||||||
pub struct Application {
|
pub struct Application {
|
||||||
|
|
@ -96,6 +98,7 @@ pub async fn init_state(config: &Config) -> State {
|
||||||
pg_pool,
|
pg_pool,
|
||||||
config: Arc::new(config.clone()),
|
config: Arc::new(config.clone()),
|
||||||
user: Arc::new(Default::default()),
|
user: Arc::new(Default::default()),
|
||||||
|
id_gen: Arc::new(RwLock::new(Snowflake::new(1))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ pub struct Credentials {
|
||||||
pub async fn validate_credentials(
|
pub async fn validate_credentials(
|
||||||
credentials: Credentials,
|
credentials: Credentials,
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
) -> Result<uuid::Uuid, AuthError> {
|
) -> Result<i64, AuthError> {
|
||||||
let mut uid = None;
|
let mut uid = None;
|
||||||
let mut expected_hash_password = Secret::new(
|
let mut expected_hash_password = Secret::new(
|
||||||
"$argon2id$v=19$m=15000,t=2,p=1$\
|
"$argon2id$v=19$m=15000,t=2,p=1$\
|
||||||
|
|
@ -58,7 +58,7 @@ pub fn compute_hash_password(password: &[u8]) -> Result<Secret<String>, anyhow::
|
||||||
async fn get_stored_credentials(
|
async fn get_stored_credentials(
|
||||||
email: &str,
|
email: &str,
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
) -> Result<Option<(uuid::Uuid, Secret<String>)>, anyhow::Error> {
|
) -> Result<Option<(i64, Secret<String>)>, anyhow::Error> {
|
||||||
let row = sqlx::query!(
|
let row = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
SELECT uid, password
|
SELECT uid, password
|
||||||
|
|
|
||||||
|
|
@ -2,51 +2,46 @@ use crate::component::auth::{
|
||||||
compute_hash_password, internal_error, validate_credentials, AuthError, Credentials,
|
compute_hash_password, internal_error, validate_credentials, AuthError, Credentials,
|
||||||
};
|
};
|
||||||
use crate::config::env::domain;
|
use crate::config::env::domain;
|
||||||
use crate::state::UserCache;
|
use crate::state::{State, UserCache};
|
||||||
use crate::telemetry::spawn_blocking_with_tracing;
|
use crate::telemetry::spawn_blocking_with_tracing;
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
|
|
||||||
use secrecy::{ExposeSecret, Secret, Zeroize};
|
use secrecy::zeroize::DefaultIsZeroes;
|
||||||
|
use secrecy::{CloneableSecret, DebugSecret, ExposeSecret, Secret, Zeroize};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::types::uuid;
|
|
||||||
use sqlx::{PgPool, Postgres, Transaction};
|
use sqlx::{PgPool, Postgres, Transaction};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use token::{create_token, parse_token, TokenError};
|
use token::{create_token, parse_token, TokenError};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
pub async fn login(
|
pub async fn login(
|
||||||
pg_pool: PgPool,
|
|
||||||
cache: Arc<RwLock<UserCache>>,
|
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
server_key: &Secret<String>,
|
state: &State,
|
||||||
) -> Result<(LoginResponse, Secret<Token>), AuthError> {
|
) -> Result<(LoginResponse, Secret<Token>), AuthError> {
|
||||||
let credentials = Credentials {
|
let credentials = Credentials {
|
||||||
email,
|
email,
|
||||||
password: Secret::new(password),
|
password: Secret::new(password),
|
||||||
};
|
};
|
||||||
|
let server_key = &state.config.application.server_key;
|
||||||
|
|
||||||
match validate_credentials(credentials, &pg_pool).await {
|
match validate_credentials(credentials, &state.pg_pool).await {
|
||||||
Ok(uid) => {
|
Ok(uid) => {
|
||||||
let uid = uid.to_string();
|
let token = Token::create_token(uid, server_key)?;
|
||||||
let token = Token::create_token(&uid, server_key)?;
|
let logged_user = LoggedUser::new(uid);
|
||||||
let logged_user = LoggedUser::new(uid.clone());
|
state.user.write().await.authorized(logged_user);
|
||||||
cache.write().await.authorized(logged_user);
|
|
||||||
Ok((
|
Ok((
|
||||||
LoginResponse {
|
LoginResponse {
|
||||||
token: token.clone().into(),
|
token: token.clone().into(),
|
||||||
uid,
|
uid: uid.to_string(),
|
||||||
},
|
},
|
||||||
Secret::new(token),
|
Secret::new(token),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => Err(err),
|
||||||
//
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,13 +50,13 @@ pub async fn logout(logged_user: LoggedUser, cache: Arc<RwLock<UserCache>>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
pg_pool: PgPool,
|
|
||||||
cache: Arc<RwLock<UserCache>>,
|
|
||||||
username: String,
|
username: String,
|
||||||
email: String,
|
email: String,
|
||||||
password: String,
|
password: String,
|
||||||
server_key: &Secret<String>,
|
state: &State,
|
||||||
) -> Result<RegisterResponse, AuthError> {
|
) -> Result<RegisterResponse, AuthError> {
|
||||||
|
let pg_pool = state.pg_pool.clone();
|
||||||
|
let server_key = &state.config.application.server_key;
|
||||||
let mut transaction = pg_pool
|
let mut transaction = pg_pool
|
||||||
.begin()
|
.begin()
|
||||||
.await
|
.await
|
||||||
|
|
@ -75,15 +70,15 @@ pub async fn register(
|
||||||
return Err(AuthError::UserAlreadyExist { email });
|
return Err(AuthError::UserAlreadyExist { email });
|
||||||
}
|
}
|
||||||
|
|
||||||
let uuid = uuid::Uuid::new_v4();
|
let uid = state.id_gen.write().await.next_id();
|
||||||
let token = Token::create_token(&uuid.to_string(), server_key)?;
|
let token = Token::create_token(uid, server_key)?;
|
||||||
let password = compute_hash_password(password.as_bytes()).map_err(internal_error)?;
|
let password = compute_hash_password(password.as_bytes()).map_err(internal_error)?;
|
||||||
let _ = sqlx::query!(
|
let _ = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO users (uid, email, username, create_time, password)
|
INSERT INTO users (uid, email, username, create_time, password)
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
"#,
|
"#,
|
||||||
uuid,
|
uid,
|
||||||
email,
|
email,
|
||||||
username,
|
username,
|
||||||
Utc::now(),
|
Utc::now(),
|
||||||
|
|
@ -100,8 +95,8 @@ pub async fn register(
|
||||||
.context("Failed to commit SQL transaction to register user.")
|
.context("Failed to commit SQL transaction to register user.")
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
|
|
||||||
let logged_user = LoggedUser::new(uuid.to_string());
|
let logged_user = LoggedUser::new(uid);
|
||||||
cache.write().await.authorized(logged_user);
|
state.user.write().await.authorized(logged_user);
|
||||||
|
|
||||||
Ok(RegisterResponse {
|
Ok(RegisterResponse {
|
||||||
token: token.into(),
|
token: token.into(),
|
||||||
|
|
@ -120,7 +115,7 @@ pub async fn change_password(
|
||||||
.context("Failed to acquire a Postgres connection to change password")
|
.context("Failed to acquire a Postgres connection to change password")
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
|
|
||||||
let email = get_user_email(logged_user.expose_secret(), &mut transaction).await?;
|
let email = get_user_email(*logged_user.expose_secret(), &mut transaction).await?;
|
||||||
|
|
||||||
// check password
|
// check password
|
||||||
let credentials = Credentials {
|
let credentials = Credentials {
|
||||||
|
|
@ -135,15 +130,11 @@ pub async fn change_password(
|
||||||
.await
|
.await
|
||||||
.context("Failed to hash password")??;
|
.context("Failed to hash password")??;
|
||||||
|
|
||||||
let uid =
|
|
||||||
uuid::Uuid::parse_str(logged_user.expose_secret()).map_err(|e| AuthError::InvalidUuid {
|
|
||||||
err: format!("{}", e),
|
|
||||||
})?;
|
|
||||||
// Save password to disk
|
// Save password to disk
|
||||||
let sql = "UPDATE users SET password = $1 where uid = $2";
|
let sql = "UPDATE users SET password = $1 where uid = $2";
|
||||||
let _ = sqlx::query(sql)
|
let _ = sqlx::query(sql)
|
||||||
.bind(new_hash_password.expose_secret())
|
.bind(new_hash_password.expose_secret())
|
||||||
.bind(uid)
|
.bind(logged_user.expose_secret())
|
||||||
.execute(&mut transaction)
|
.execute(&mut transaction)
|
||||||
.await
|
.await
|
||||||
.context("Failed to change user's password in the database.")?;
|
.context("Failed to change user's password in the database.")?;
|
||||||
|
|
@ -157,10 +148,9 @@ pub async fn change_password(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_user_email(
|
pub async fn get_user_email(
|
||||||
uid: &str,
|
uid: i64,
|
||||||
transaction: &mut Transaction<'_, Postgres>,
|
transaction: &mut Transaction<'_, Postgres>,
|
||||||
) -> Result<String, anyhow::Error> {
|
) -> Result<String, anyhow::Error> {
|
||||||
let uid = uuid::Uuid::parse_str(uid)?;
|
|
||||||
let row = sqlx::query!(
|
let row = sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
SELECT email
|
SELECT email
|
||||||
|
|
@ -219,37 +209,47 @@ pub struct ChangePasswordRequest {
|
||||||
pub new_password_confirm: String,
|
pub new_password_confirm: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LoggedUser {
|
pub struct WrapI64(i64);
|
||||||
uid: Secret<String>,
|
impl Default for WrapI64 {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
impl Copy for WrapI64 {}
|
||||||
|
impl DefaultIsZeroes for WrapI64 {}
|
||||||
|
impl DebugSecret for WrapI64 {}
|
||||||
|
impl CloneableSecret for WrapI64 {}
|
||||||
|
|
||||||
|
impl std::ops::Deref for WrapI64 {
|
||||||
|
type Target = i64;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LoggedUser(Secret<WrapI64>);
|
||||||
|
|
||||||
impl From<Claim> for LoggedUser {
|
impl From<Claim> for LoggedUser {
|
||||||
fn from(c: Claim) -> Self {
|
fn from(c: Claim) -> Self {
|
||||||
Self {
|
Self(Secret::new(WrapI64(c.uid)))
|
||||||
uid: Secret::new(c.uid),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoggedUser {
|
impl LoggedUser {
|
||||||
pub fn new(uid: String) -> Self {
|
pub fn new(uid: i64) -> Self {
|
||||||
Self {
|
Self(Secret::new(WrapI64(uid)))
|
||||||
uid: Secret::new(uid),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_token(server_key: &Secret<String>, token: &str) -> Result<Self, AuthError> {
|
pub fn from_token(server_key: &Secret<String>, token: &str) -> Result<Self, AuthError> {
|
||||||
let user: LoggedUser = Token::decode_token(server_key, token)?.into();
|
let user: LoggedUser = Token::decode_token(server_key, token)?.into();
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for LoggedUser {
|
pub fn expose_secret(&self) -> &i64 {
|
||||||
type Target = Secret<String>;
|
self.0.expose_secret()
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.uid
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,15 +259,12 @@ pub const EXPIRED_DURATION_DAYS: i64 = 30;
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Claim {
|
pub struct Claim {
|
||||||
iss: String,
|
iss: String,
|
||||||
uid: String,
|
uid: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Claim {
|
impl Claim {
|
||||||
pub fn with_user_id(uid: &str) -> Self {
|
pub fn with_user_id(uid: i64) -> Self {
|
||||||
Self {
|
Self { iss: domain(), uid }
|
||||||
iss: domain(),
|
|
||||||
uid: uid.to_string(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,8 +278,8 @@ impl Zeroize for Token {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn create_token(user_id: &str, server_key: &Secret<String>) -> Result<Self, AuthError> {
|
pub fn create_token(uid: i64, server_key: &Secret<String>) -> Result<Self, AuthError> {
|
||||||
let claim = Claim::with_user_id(user_id);
|
let claim = Claim::with_user_id(uid);
|
||||||
let token = create_token(
|
let token = create_token(
|
||||||
server_key.expose_secret().as_str(),
|
server_key.expose_secret().as_str(),
|
||||||
claim,
|
claim,
|
||||||
|
|
@ -317,7 +314,7 @@ pub fn logged_user_from_request(
|
||||||
pub fn uid_from_request(
|
pub fn uid_from_request(
|
||||||
request: &HttpRequest,
|
request: &HttpRequest,
|
||||||
server_key: &Secret<String>,
|
server_key: &Secret<String>,
|
||||||
) -> Result<Secret<String>, AuthError> {
|
) -> Result<Secret<i64>, AuthError> {
|
||||||
match request.headers().get(HEADER_TOKEN) {
|
match request.headers().get(HEADER_TOKEN) {
|
||||||
Some(header) => match header.to_str() {
|
Some(header) => match header.to_str() {
|
||||||
Ok(val) => Token::decode_token(server_key, val).map(|claim| Secret::new(claim.uid)),
|
Ok(val) => Token::decode_token(server_key, val).map(|claim| Secret::new(claim.uid)),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::component::auth::LoggedUser;
|
use crate::component::auth::LoggedUser;
|
||||||
use crate::config::config::Config;
|
use crate::config::config::Config;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use secrecy::ExposeSecret;
|
use snowflake::Snowflake;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -12,12 +12,17 @@ pub struct State {
|
||||||
pub pg_pool: PgPool,
|
pub pg_pool: PgPool,
|
||||||
pub config: Arc<Config>,
|
pub config: Arc<Config>,
|
||||||
pub user: Arc<RwLock<UserCache>>,
|
pub user: Arc<RwLock<UserCache>>,
|
||||||
|
pub id_gen: Arc<RwLock<Snowflake>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
pub async fn load_users(_pool: &PgPool) {
|
pub async fn load_users(_pool: &PgPool) {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn next_user_id(&self) -> i64 {
|
||||||
|
self.id_gen.write().await.next_id()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Copy)]
|
#[derive(Clone, Debug, Copy)]
|
||||||
|
|
@ -31,7 +36,7 @@ pub const EXPIRED_DURATION_DAYS: i64 = 30;
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct UserCache {
|
pub struct UserCache {
|
||||||
// Keep track the user authentication state
|
// Keep track the user authentication state
|
||||||
user: BTreeMap<String, AuthStatus>,
|
user: BTreeMap<i64, AuthStatus>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserCache {
|
impl UserCache {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue