mirror of
https://github.com/aljazceru/pubky-core.git
synced 2025-12-31 12:54:35 +01:00
feat(homeserver): check session exists
This commit is contained in:
153
Cargo.lock
generated
153
Cargo.lock
generated
@@ -125,6 +125,29 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-extra"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0be6ea09c9b96cb5076af0de2e383bd2bc0c18f827cf1967bdd353e0b910d733"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"headers",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.73"
|
||||
@@ -142,9 +165,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "base32"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d1ce0365f4d5fb6646220bb52fe547afd51796d90f914d4063cb0b032ebee088"
|
||||
checksum = "022dfe9eb35f19ebbcb51e0b40a5ab759f46ad60cadf7297e0bd085afb50e076"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
@@ -246,6 +275,17 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
@@ -347,6 +387,15 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@@ -603,6 +652,30 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"bytes",
|
||||
"headers-core",
|
||||
"http",
|
||||
"httpdate",
|
||||
"mime",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "headers-core"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4"
|
||||
dependencies = [
|
||||
"http",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
@@ -913,6 +986,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
@@ -1105,6 +1184,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
@@ -1153,6 +1238,8 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"base32",
|
||||
"bytes",
|
||||
"dirs-next",
|
||||
"heed",
|
||||
@@ -1161,6 +1248,7 @@ dependencies = [
|
||||
"pubky-common",
|
||||
"serde",
|
||||
"tokio",
|
||||
"tower-cookies",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@@ -1433,6 +1521,17 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
@@ -1610,6 +1709,37 @@ dependencies = [
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
@@ -1671,6 +1801,23 @@ dependencies = [
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-cookies"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fd0118512cf0b3768f7fcccf0bef1ae41d68f2b45edc1e77432b36c97c56c6d"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"cookie",
|
||||
"futures-util",
|
||||
"http",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-http"
|
||||
version = "0.5.2"
|
||||
@@ -1801,7 +1948,7 @@ version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"flate2",
|
||||
"log",
|
||||
"once_cell",
|
||||
|
||||
@@ -12,3 +12,12 @@ pub fn random_hash() -> Hash {
|
||||
let mut rng = rand::thread_rng();
|
||||
Hash::from_bytes(rng.gen())
|
||||
}
|
||||
|
||||
pub fn random_bytes<const N: usize>() -> [u8; N] {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut arr = [0u8; N];
|
||||
for i in 0..N {
|
||||
arr[i] = rng.gen();
|
||||
}
|
||||
arr
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ edition = "2021"
|
||||
[dependencies]
|
||||
anyhow = "1.0.82"
|
||||
axum = "0.7.5"
|
||||
axum-extra = { version = "0.9.3", features = ["typed-header"] }
|
||||
base32 = "0.5.1"
|
||||
bytes = "1.6.1"
|
||||
dirs-next = "2.0.0"
|
||||
heed = "0.20.3"
|
||||
@@ -14,6 +16,7 @@ postcard = { version = "1.0.8", features = ["alloc"] }
|
||||
pubky-common = { version = "0.1.0", path = "../pubky-common" }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tower-cookies = "0.10.0"
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
tracing = "0.1.40"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
|
||||
@@ -30,6 +30,7 @@ impl DB {
|
||||
let mut wtxn = self.env.write_txn()?;
|
||||
|
||||
migrations::create_users_table(&self.env, &mut wtxn);
|
||||
migrations::create_sessions_table(&self.env, &mut wtxn);
|
||||
|
||||
wtxn.commit()?;
|
||||
|
||||
|
||||
@@ -2,10 +2,18 @@ use heed::{types::Str, Database, Env, RwTxn};
|
||||
|
||||
use super::tables;
|
||||
|
||||
pub const TABLES_COUNT: u32 = 1;
|
||||
pub const TABLES_COUNT: u32 = 2;
|
||||
|
||||
pub fn create_users_table(env: &Env, wtxn: &mut RwTxn) -> anyhow::Result<()> {
|
||||
let _: tables::users::UsersTable = env.create_database(wtxn, None)?;
|
||||
let _: tables::users::UsersTable =
|
||||
env.create_database(wtxn, Some(tables::users::USERS_TABLE))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_sessions_table(env: &Env, wtxn: &mut RwTxn) -> anyhow::Result<()> {
|
||||
let _: tables::sessions::SessionsTable =
|
||||
env.create_database(wtxn, Some(tables::sessions::SESSIONS_TABLE))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
pub mod sessions;
|
||||
pub mod users;
|
||||
|
||||
42
pubky-homeserver/src/database/tables/sessions.rs
Normal file
42
pubky-homeserver/src/database/tables/sessions.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::{borrow::Cow, time::SystemTime};
|
||||
|
||||
use postcard::{from_bytes, to_allocvec};
|
||||
use pubky_common::timestamp::Timestamp;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use heed::{types::Bytes, BoxedError, BytesDecode, BytesEncode, Database};
|
||||
use pkarr::PublicKey;
|
||||
|
||||
extern crate alloc;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// session secret => Session.
|
||||
pub type SessionsTable = Database<Bytes, Session>;
|
||||
|
||||
pub const SESSIONS_TABLE: &str = "sessions";
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq)]
|
||||
pub struct Session {
|
||||
pub created_at: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
impl<'a> BytesEncode<'a> for Session {
|
||||
type EItem = Self;
|
||||
|
||||
fn bytes_encode(session: &Self::EItem) -> Result<Cow<[u8]>, BoxedError> {
|
||||
let vec = to_allocvec(session)?;
|
||||
|
||||
Ok(Cow::Owned(vec))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> BytesDecode<'a> for Session {
|
||||
type DItem = Self;
|
||||
|
||||
fn bytes_decode(bytes: &'a [u8]) -> Result<Self::DItem, BoxedError> {
|
||||
let sesison: Session = from_bytes(bytes).unwrap();
|
||||
|
||||
Ok(sesison)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use axum::{
|
||||
routing::{get, post, put},
|
||||
Router,
|
||||
};
|
||||
use tower_cookies::CookieManagerLayer;
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::server::AppState;
|
||||
@@ -14,7 +15,9 @@ pub fn create_app(state: AppState) -> Router {
|
||||
Router::new()
|
||||
.route("/", get(root::handler))
|
||||
.route("/:pubky", put(auth::signup))
|
||||
.route("/:pubky/session", get(auth::session))
|
||||
.route("/:pubky/*key", get(drive::put))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(CookieManagerLayer::new())
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -1,32 +1,102 @@
|
||||
use axum::{extract::State, response::IntoResponse};
|
||||
use axum::{
|
||||
extract::{Request, State},
|
||||
http::{HeaderMap, StatusCode},
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_extra::{headers::UserAgent, TypedHeader};
|
||||
use bytes::Bytes;
|
||||
use tower_cookies::{Cookie, Cookies};
|
||||
|
||||
use pubky_common::timestamp::Timestamp;
|
||||
use pubky_common::{
|
||||
crypto::{random_bytes, random_hash},
|
||||
timestamp::Timestamp,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
database::tables::users::{User, UsersTable, USERS_TABLE},
|
||||
error::Result,
|
||||
database::tables::{
|
||||
sessions::{Session, SessionsTable, SESSIONS_TABLE},
|
||||
users::{User, UsersTable, USERS_TABLE},
|
||||
},
|
||||
error::{Error, Result},
|
||||
extractors::Pubky,
|
||||
server::AppState,
|
||||
};
|
||||
|
||||
pub async fn signup(
|
||||
State(state): State<AppState>,
|
||||
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
||||
cookies: Cookies,
|
||||
pubky: Pubky,
|
||||
body: Bytes,
|
||||
) -> Result<impl IntoResponse> {
|
||||
state.verifier.verify(&body, pubky.public_key())?;
|
||||
let public_key = pubky.public_key();
|
||||
|
||||
state.verifier.verify(&body, public_key)?;
|
||||
|
||||
let mut wtxn = state.db.env.write_txn()?;
|
||||
let users: UsersTable = state.db.env.create_database(&mut wtxn, Some(USERS_TABLE))?;
|
||||
|
||||
users.put(
|
||||
&mut wtxn,
|
||||
pubky.public_key(),
|
||||
public_key,
|
||||
&User {
|
||||
created_at: Timestamp::now().into_inner(),
|
||||
},
|
||||
)?;
|
||||
|
||||
let session_secret = random_bytes::<16>();
|
||||
|
||||
let sessions: SessionsTable = state
|
||||
.db
|
||||
.env
|
||||
.open_database(&wtxn, Some(SESSIONS_TABLE))?
|
||||
.expect("Sessions table already created");
|
||||
|
||||
// TODO: handle not having a user agent?
|
||||
let session = &Session {
|
||||
created_at: Timestamp::now().into_inner(),
|
||||
name: user_agent.to_string(),
|
||||
};
|
||||
|
||||
sessions.put(&mut wtxn, &session_secret, session)?;
|
||||
|
||||
cookies.add(Cookie::new(
|
||||
public_key.to_string(),
|
||||
base32::encode(base32::Alphabet::Crockford, &session_secret),
|
||||
));
|
||||
|
||||
wtxn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn session(
|
||||
State(state): State<AppState>,
|
||||
TypedHeader(user_agent): TypedHeader<UserAgent>,
|
||||
cookies: Cookies,
|
||||
pubky: Pubky,
|
||||
) -> Result<impl IntoResponse> {
|
||||
if let Some(cookie) = cookies.get(&pubky.public_key().to_string()) {
|
||||
let rtxn = state.db.env.read_txn()?;
|
||||
|
||||
let sessions: SessionsTable = state
|
||||
.db
|
||||
.env
|
||||
.open_database(&rtxn, Some(SESSIONS_TABLE))?
|
||||
.expect("Session table already created");
|
||||
|
||||
if let Some(session) = sessions.get(
|
||||
&rtxn,
|
||||
&base32::decode(base32::Alphabet::Crockford, cookie.value()).unwrap_or_default(),
|
||||
)? {
|
||||
rtxn.commit()?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
rtxn.commit()?;
|
||||
};
|
||||
|
||||
Err(Error::with_status(StatusCode::NOT_FOUND))
|
||||
}
|
||||
|
||||
@@ -63,6 +63,20 @@ impl PubkyClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the current sesison for a given Pubky in its homeserver.
|
||||
pub fn session(&self, pubky: &PublicKey) -> Result<()> {
|
||||
let (homeserver, mut url) = self.resolve_pubky_homeserver(pubky)?;
|
||||
|
||||
url.set_path(&format!("/{}/sesison", pubky));
|
||||
|
||||
let response = self
|
||||
.request(HttpMethod::Get, &url)
|
||||
.call()
|
||||
.map_err(Box::new)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// === Private Methods ===
|
||||
|
||||
/// Publish the SVCB record for `_pubky.<public_key>`.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::thread;
|
||||
|
||||
use pkarr::Keypair;
|
||||
use pkarr::{Keypair, PublicKey};
|
||||
|
||||
use crate::{error::Result, PubkyClient};
|
||||
|
||||
@@ -28,4 +28,19 @@ impl PubkyClientAsync {
|
||||
|
||||
receiver.recv_async().await?
|
||||
}
|
||||
|
||||
/// Async version of [PubkyClient::session]
|
||||
pub async fn session(&self, pubky: &PublicKey) -> Result<()> {
|
||||
let (sender, receiver) = flume::bounded::<Result<()>>(1);
|
||||
|
||||
let client = self.0.clone();
|
||||
let pubky = pubky.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let result = client.session(&pubky);
|
||||
sender.send(result)
|
||||
});
|
||||
|
||||
receiver.recv_async().await?
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ mod tests {
|
||||
client
|
||||
.signup(&keypair, &server.public_key().to_string())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let session = client.session(&keypair.public_key()).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user