You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
107 lines
2.5 KiB
107 lines
2.5 KiB
//! Used to determine which post belongs to who.
|
|
//!
|
|
//! Mostly for determining if a poster owns a post.
|
|
use super::*;
|
|
|
|
use std::{
|
|
net::SocketAddr,
|
|
};
|
|
use cryptohelpers::sha256;
|
|
|
|
/// A user's unique ID.
|
|
///
|
|
/// This is composed by the user's address and their session ID.
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
|
pub struct UserID(SocketAddr, session::SessionID);
|
|
|
|
static COUNTER: GlobalCounter = GlobalCounter::new();
|
|
|
|
impl UserID
|
|
{
|
|
/// Generate a token from this instance.
|
|
///
|
|
/// User tokens are deterministically generated and can be deterministically verified.
|
|
pub fn generate_token(&self) -> u64
|
|
{
|
|
let cnt = COUNTER.get();
|
|
let mut trunc = [0u8; std::mem::size_of::<u64>()];
|
|
|
|
let hash = GloballySalted::new(self).compute_sha256_hash();
|
|
bytes::move_slice(&mut trunc[..], hash.as_ref());
|
|
|
|
u64::from_le_bytes(trunc) ^ cnt
|
|
}
|
|
|
|
/// Validate a token for this instance created with `generate_token`.
|
|
pub fn validate_token(&self, val: u64) -> bool
|
|
{
|
|
let mut trunc = [0u8; std::mem::size_of::<u64>()];
|
|
|
|
let hash = GloballySalted::new(self).compute_sha256_hash();
|
|
bytes::move_slice(&mut trunc[..], hash.as_ref());
|
|
|
|
COUNTER.valid(u64::from_le_bytes(trunc) ^ val)
|
|
}
|
|
}
|
|
|
|
/// A user not bound to a session.
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
|
pub struct User
|
|
{
|
|
addr: SocketAddr,
|
|
}
|
|
|
|
impl User
|
|
{
|
|
/// Get the user ID for this session.
|
|
pub fn id_for_session(&self, session: &session::Session) -> UserID
|
|
{
|
|
UserID(self.addr, session.session_id().clone())
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests
|
|
{
|
|
use super::*;
|
|
use tokio::sync::mpsc;
|
|
use tokio::time;
|
|
use std::net::SocketAddrV4;
|
|
use std::net::Ipv4Addr;
|
|
#[tokio::test]
|
|
async fn counter_tokens()
|
|
{
|
|
let addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 80));
|
|
let usr = User{addr};
|
|
let ses = session::Session::create(usr);
|
|
|
|
let id = ses.user_id();
|
|
|
|
let (mut tx, mut rx) = mpsc::channel(5);
|
|
let task = tokio::spawn(async move {
|
|
let id = ses.user_id();
|
|
|
|
while let Some(token) = rx.recv().await {
|
|
if !id.validate_token(token) {
|
|
panic!("Failed to validate token {:x} for id {:?}", token, id);
|
|
} else {
|
|
eprintln!("Token {:x} valid for id {:?}", token, id);
|
|
}
|
|
}
|
|
});
|
|
|
|
for x in 1..=10
|
|
{
|
|
if x % 2 == 0 {
|
|
time::delay_for(time::Duration::from_millis(10 * x)).await;
|
|
}
|
|
if tx.send(id.generate_token()).await.is_err() {
|
|
eprintln!("Failed to send to task");
|
|
break;
|
|
}
|
|
}
|
|
drop(tx);
|
|
task.await.expect("Background validate task failed");
|
|
}
|
|
}
|