server side preparation
This commit is contained in:
48
src/bin/server/db/data.rs
Normal file
48
src/bin/server/db/data.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use iris::core::util::HashSet;
|
||||
pub use openworm::net::{AccountToken, ServerPerms};
|
||||
|
||||
pub type UserId = u64;
|
||||
pub type MsgId = i128;
|
||||
pub type ChannelId = u64;
|
||||
pub type ImageId = u64;
|
||||
|
||||
#[derive(bitcode::Encode, bitcode::Decode)]
|
||||
pub struct User {
|
||||
pub username: String,
|
||||
pub password_hash: String,
|
||||
pub pfp: Option<ImageId>,
|
||||
pub bio: String,
|
||||
pub friends: Friends,
|
||||
pub server_perms: ServerPerms,
|
||||
}
|
||||
|
||||
impl User {
|
||||
pub fn new(username: String, password_hash: String, perms: ServerPerms) -> Self {
|
||||
Self {
|
||||
username,
|
||||
password_hash,
|
||||
bio: String::new(),
|
||||
pfp: None,
|
||||
friends: Default::default(),
|
||||
server_perms: perms,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, bitcode::Encode, bitcode::Decode)]
|
||||
pub struct Friends {
|
||||
pub current: HashSet<UserId>,
|
||||
pub outgoing: HashSet<UserId>,
|
||||
pub incoming: HashSet<UserId>,
|
||||
}
|
||||
|
||||
#[derive(bitcode::Encode, bitcode::Decode)]
|
||||
pub struct Msg {
|
||||
pub content: String,
|
||||
pub author: UserId,
|
||||
}
|
||||
|
||||
pub struct Channel {
|
||||
pub name: String,
|
||||
pub desc: String,
|
||||
}
|
||||
@@ -1,31 +1,22 @@
|
||||
mod data;
|
||||
mod util;
|
||||
mod ver;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
pub use data::*;
|
||||
use util::*;
|
||||
use ver::*;
|
||||
|
||||
pub const DB_VERSION: u64 = 0;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Db {
|
||||
db: Database,
|
||||
pub account_tokens: DbMap<String, ServerPerms>,
|
||||
pub account_tokens: DbMap<AccountToken, ServerPerms>,
|
||||
pub msgs: DbMap<MsgId, Msg>,
|
||||
pub users: DbMap<UserId, User>,
|
||||
pub usernames: DbMap<String, UserId>,
|
||||
}
|
||||
|
||||
pub type UserId = UserIdV0;
|
||||
pub type MsgId = MsgIdV0;
|
||||
pub type ChannelId = ChannelIdV0;
|
||||
pub type ImageId = ImageIdV0;
|
||||
pub type User = UserV0;
|
||||
pub type Msg = MsgV0;
|
||||
pub type ChannelInfo = ChannelV0;
|
||||
pub type ServerPerms = ServerPermsV0;
|
||||
|
||||
impl Db {
|
||||
pub fn open(path: impl AsRef<Path>) -> Db {
|
||||
let db = Database::open(path);
|
||||
|
||||
@@ -17,7 +17,7 @@ impl<K, V> Clone for DbMap<K, V> {
|
||||
Self {
|
||||
db: self.db.clone(),
|
||||
keyspace: self.keyspace.clone(),
|
||||
_pd: self._pd.clone(),
|
||||
_pd: self._pd,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +126,7 @@ impl WriteTx {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get<K: Key<Output = K>, V: DecodeOwned>(&self, map: &DbMap<K, V>, k: K) -> Option<V> {
|
||||
pub fn get<K: Key<Output = K>, V: DecodeOwned>(&self, map: &DbMap<K, V>, k: &K) -> Option<V> {
|
||||
let k = Slice::new(k.to_bytes().as_ref());
|
||||
let v = self.0.get(&map.keyspace, k).unwrap()?;
|
||||
Some(bitcode::decode(&v).unwrap())
|
||||
@@ -137,7 +137,11 @@ impl WriteTx {
|
||||
self.0.get(&map.keyspace, k).unwrap().is_some()
|
||||
}
|
||||
|
||||
pub fn remove<K: Key<Output = K>, V: DecodeOwned>(&mut self, map: &DbMap<K, V>, k: K) -> Option<V> {
|
||||
pub fn remove<K: Key<Output = K>, V: DecodeOwned>(
|
||||
&mut self,
|
||||
map: &DbMap<K, V>,
|
||||
k: K,
|
||||
) -> Option<V> {
|
||||
let k = Slice::new(k.to_bytes().as_ref());
|
||||
let v = self.0.take(&map.keyspace, k).unwrap()?;
|
||||
Some(bitcode::decode(&v).unwrap())
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
pub type UserIdV0 = u64;
|
||||
pub type MsgIdV0 = i128;
|
||||
pub type ChannelIdV0 = u64;
|
||||
pub type ImageIdV0 = u64;
|
||||
|
||||
#[derive(Clone, Copy, bitcode::Encode, bitcode::Decode)]
|
||||
pub struct ServerPermsV0(u32);
|
||||
impl ServerPermsV0 {
|
||||
pub const NONE: Self = Self(0);
|
||||
pub const ACCOUNT_TOKENS: Self = Self(1 << 0);
|
||||
pub const ALL: Self = Self(u32::MAX);
|
||||
}
|
||||
|
||||
#[derive(bitcode::Encode, bitcode::Decode)]
|
||||
pub struct UserV0 {
|
||||
pub username: String,
|
||||
pub password_hash: String,
|
||||
pub pfp: Option<ImageIdV0>,
|
||||
pub bio: String,
|
||||
pub server_perms: ServerPermsV0,
|
||||
}
|
||||
|
||||
#[derive(bitcode::Encode, bitcode::Decode)]
|
||||
pub struct MsgV0 {
|
||||
pub content: String,
|
||||
pub author: UserIdV0,
|
||||
}
|
||||
|
||||
pub struct ChannelV0 {
|
||||
pub name: String,
|
||||
pub desc: String,
|
||||
}
|
||||
|
||||
impl ServerPermsV0 {
|
||||
pub fn contains(&self, other: Self) -> bool {
|
||||
(self.0 & other.0) == other.0
|
||||
}
|
||||
}
|
||||
313
src/bin/server/handle.rs
Normal file
313
src/bin/server/handle.rs
Normal file
@@ -0,0 +1,313 @@
|
||||
use super::net::ClientSender;
|
||||
use crate::{
|
||||
ClientId, account_token,
|
||||
db::{Db, ServerPerms, User, UserId},
|
||||
net::ClientReplier,
|
||||
};
|
||||
use openworm::net::*;
|
||||
use scrypt::{
|
||||
Scrypt,
|
||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ClientState {
|
||||
Login,
|
||||
Authed(UserId),
|
||||
}
|
||||
|
||||
pub struct ClientHandler {
|
||||
pub db: Db,
|
||||
pub send: ClientSender,
|
||||
pub senders: Arc<RwLock<HashMap<ClientId, ClientSender>>>,
|
||||
pub id: ClientId,
|
||||
pub state: Arc<RwLock<ClientState>>,
|
||||
}
|
||||
|
||||
impl ClientHandler {
|
||||
pub async fn handle_msg(&self, msg: ClientMsg, replier: &ClientReplier) -> Option<()> {
|
||||
// TODO: there are some awful edge cases where you delete an account and it's in the middle
|
||||
// of a non TX transaction; make sure to use TX, maybe need another check_login closure
|
||||
// fortunately these should be very rare so I'ma ignore for now
|
||||
macro_rules! reply {
|
||||
($msg:expr) => {
|
||||
replier.send($msg).await;
|
||||
return None;
|
||||
};
|
||||
}
|
||||
let check_login = async || {
|
||||
if let ClientState::Authed(uid) = &*self.state.read().await {
|
||||
Some(*uid)
|
||||
} else {
|
||||
reply!(NotLoggedIn);
|
||||
}
|
||||
};
|
||||
let check_user = async || {
|
||||
if let ClientState::Authed(uid) = &*self.state.read().await {
|
||||
if let Some(user) = self.db.users.get(uid) {
|
||||
Some((*uid, user))
|
||||
} else {
|
||||
reply!(AccountDeleted);
|
||||
}
|
||||
} else {
|
||||
reply!(NotLoggedIn);
|
||||
}
|
||||
};
|
||||
let check_server_perms = async |perms| {
|
||||
let id = check_login().await?;
|
||||
if self
|
||||
.db
|
||||
.users
|
||||
.get(&id)
|
||||
.is_some_and(|u| !u.server_perms.contains(perms))
|
||||
{
|
||||
reply!(NoPermission);
|
||||
} else {
|
||||
Some(())
|
||||
}
|
||||
};
|
||||
let db = &self.db;
|
||||
match msg {
|
||||
ClientMsg::GenerateToken(info) => {
|
||||
let check = if info.perms == ServerPerms::NONE {
|
||||
ServerPerms::ACCOUNT_TOKENS
|
||||
} else {
|
||||
ServerPerms::ALL
|
||||
};
|
||||
check_server_perms(check).await?;
|
||||
let token = account_token(&self.db, info.perms);
|
||||
reply!(GenerateTokenResp { token });
|
||||
}
|
||||
ClientMsg::CreateAccount(info) => {
|
||||
let CreateAccount {
|
||||
token,
|
||||
username,
|
||||
password,
|
||||
} = &info;
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let params = scrypt::Params::new(11, 8, 1, 32).unwrap();
|
||||
let hash = Scrypt
|
||||
.hash_password_customized(password.as_bytes(), None, None, params, &salt)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let mut id;
|
||||
loop {
|
||||
let mut tx = db.write_tx();
|
||||
let Some(perms) = tx.remove(&db.account_tokens, token.to_string()) else {
|
||||
println!("invalid token: {:?}", self.send.remote());
|
||||
reply!(CreateAccountResp::InvalidToken);
|
||||
};
|
||||
if tx.has_key(&db.usernames, username.clone()) {
|
||||
reply!(CreateAccountResp::UsernameExists);
|
||||
}
|
||||
id = rand::random();
|
||||
while tx.has_key(&db.users, id) {
|
||||
id = rand::random();
|
||||
}
|
||||
tx.insert(
|
||||
&db.users,
|
||||
&id,
|
||||
&User::new(username.clone(), hash.clone(), perms),
|
||||
);
|
||||
tx.insert(&db.usernames, username, &id);
|
||||
if tx.commit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("account created: \"{username}\"");
|
||||
*self.state.write().await = ClientState::Authed(id);
|
||||
reply!(CreateAccountResp::Ok { id });
|
||||
}
|
||||
ClientMsg::Login(info) => {
|
||||
let Login { username, password } = &info;
|
||||
let Some(id) = db.usernames.get(username) else {
|
||||
reply!(LoginResp::UnknownUsername);
|
||||
};
|
||||
let Some(user) = db.users.get(&id) else {
|
||||
panic!("invalid state! (should be a user)");
|
||||
};
|
||||
let hash = PasswordHash::new(&user.password_hash).unwrap();
|
||||
if Scrypt.verify_password(password.as_bytes(), &hash).is_err() {
|
||||
println!("invalid password: \"{username}\"");
|
||||
reply!(LoginResp::InvalidPassword);
|
||||
}
|
||||
*self.state.write().await = ClientState::Authed(id);
|
||||
reply!(LoginResp::Ok { id });
|
||||
}
|
||||
ClientMsg::RequestUsers(_) => {
|
||||
check_login().await?;
|
||||
check_server_perms(ServerPerms::ALL).await?;
|
||||
let users: Vec<_> = self
|
||||
.db
|
||||
.users
|
||||
.iter()
|
||||
.map(|(id, u)| ServerUser {
|
||||
id,
|
||||
username: u.username,
|
||||
})
|
||||
.collect();
|
||||
reply!(RequestUsersResp { users });
|
||||
}
|
||||
ClientMsg::AddFriend(info) => {
|
||||
let user_id = check_login().await?;
|
||||
loop {
|
||||
let mut tx = db.write_tx();
|
||||
let Some(mut user) = tx.get(&db.users, &user_id) else {
|
||||
reply!(AccountDeleted);
|
||||
};
|
||||
let Some(friend_id) = tx.get(&db.usernames, &info.username) else {
|
||||
reply!(AddFriendResp::UnknownUser);
|
||||
};
|
||||
if user.friends.outgoing.contains(&friend_id) {
|
||||
reply!(AddFriendResp::AlreadySent);
|
||||
}
|
||||
if friend_id == user_id {
|
||||
reply!(AddFriendResp::CannotAddSelf);
|
||||
}
|
||||
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
||||
println!("WARNING: username without valid user!");
|
||||
reply!(AddFriendResp::UnknownUser);
|
||||
};
|
||||
if other.friends.current.contains(&user_id) {
|
||||
reply!(AddFriendResp::AlreadyFriends);
|
||||
}
|
||||
user.friends.outgoing.insert(friend_id);
|
||||
other.friends.incoming.insert(user_id);
|
||||
tx.insert(&db.users, &user_id, &user);
|
||||
tx.insert(&db.users, &friend_id, &other);
|
||||
if tx.commit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reply!(AddFriendResp::Ok);
|
||||
}
|
||||
ClientMsg::RemoveFriend(info) => {
|
||||
let user_id = check_login().await?;
|
||||
loop {
|
||||
let mut tx = db.write_tx();
|
||||
let mut user = tx.get(&db.users, &user_id)?;
|
||||
let friend_id = info.id;
|
||||
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
||||
println!("WARNING: username without valid user!");
|
||||
return None;
|
||||
};
|
||||
user.friends.current.remove(&friend_id);
|
||||
other.friends.current.remove(&user_id);
|
||||
tx.insert(&db.users, &user_id, &user);
|
||||
tx.insert(&db.users, &friend_id, &other);
|
||||
if tx.commit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reply!(AddFriendResp::Ok);
|
||||
}
|
||||
ClientMsg::RequestFriends(_) => {
|
||||
let user = check_user().await?.1;
|
||||
reply!(RequestFriendsResp {
|
||||
current: user.friends.current,
|
||||
incoming: user.friends.incoming,
|
||||
outgoing: user.friends.outgoing,
|
||||
});
|
||||
}
|
||||
ClientMsg::AnswerFriendRequest(answer) => {
|
||||
let user_id = check_login().await?;
|
||||
loop {
|
||||
let mut tx = db.write_tx();
|
||||
let mut user = tx.get(&db.users, &user_id)?;
|
||||
let other_id = answer.id;
|
||||
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
||||
println!("WARNING: username without valid user!");
|
||||
return None;
|
||||
};
|
||||
match answer.action {
|
||||
FriendRequestAction::Accept => {
|
||||
user.friends.incoming.remove(&other_id);
|
||||
other.friends.outgoing.remove(&user_id);
|
||||
user.friends.current.insert(other_id);
|
||||
other.friends.current.insert(user_id);
|
||||
}
|
||||
FriendRequestAction::Deny => {
|
||||
user.friends.incoming.remove(&other_id);
|
||||
other.friends.outgoing.remove(&user_id);
|
||||
}
|
||||
}
|
||||
tx.insert(&db.users, &user_id, &user);
|
||||
tx.insert(&db.users, &other_id, &other);
|
||||
if tx.commit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RecvHandler<ClientMsgInst> for ClientHandler {
|
||||
async fn connect(&self) -> () {}
|
||||
async fn msg(&self, req: ClientMsgInst) {
|
||||
let replier = self.send.replier(req.id);
|
||||
self.handle_msg(req.msg, &replier).await;
|
||||
}
|
||||
|
||||
async fn disconnect(&self, reason: DisconnectReason) -> () {
|
||||
match reason {
|
||||
DisconnectReason::Closed | DisconnectReason::Timeout => (),
|
||||
DisconnectReason::Other(e) => println!("connection issue: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ClientMsg::SendMsg(msg) => {
|
||||
// let uid = check_login().await?;
|
||||
// let msg = Msg {
|
||||
// author: uid,
|
||||
// content: msg.content,
|
||||
// };
|
||||
// // TODO: it is technically possible to send 2 messages at the exact same time...
|
||||
// // should probably append a number if one already exists at that time,
|
||||
// // but also I can't see this ever happening...?
|
||||
// // should be an easy fix later (write tx)
|
||||
// let timestamp = time::OffsetDateTime::now_utc().unix_timestamp_nanos();
|
||||
// self.db.msgs.insert(×tamp, &msg);
|
||||
// let mut handles = Vec::new();
|
||||
// let msg = LoadMsg {
|
||||
// content: msg.content,
|
||||
// author: uid,
|
||||
// };
|
||||
// for (&id, send) in self.senders.read().await.iter() {
|
||||
// if id == self.id {
|
||||
// continue;
|
||||
// }
|
||||
// let send = send.clone();
|
||||
// let msg = msg.clone();
|
||||
// let fut = async move {
|
||||
// let _ = send
|
||||
// .send(LoadMsg {
|
||||
// content: msg.content,
|
||||
// author: msg.author,
|
||||
// })
|
||||
// .await;
|
||||
// };
|
||||
// handles.push(tokio::spawn(fut));
|
||||
// }
|
||||
// for h in handles {
|
||||
// h.await.unwrap();
|
||||
// }
|
||||
// None
|
||||
// }
|
||||
// ClientMsg::RequestMsgs => {
|
||||
// check_login().await?;
|
||||
// let msgs = self
|
||||
// .db
|
||||
// .msgs
|
||||
// .values()
|
||||
// .map(|msg| LoadMsg {
|
||||
// content: msg.content,
|
||||
// author: msg.author,
|
||||
// })
|
||||
// .collect();
|
||||
// replier.send(ServerMsg::LoadMsgs(msgs)).await;
|
||||
// }
|
||||
@@ -1,22 +1,18 @@
|
||||
mod db;
|
||||
mod handle;
|
||||
mod net;
|
||||
|
||||
use crate::db::{Db, Msg, ServerPerms, User};
|
||||
use crate::{
|
||||
db::{Db, ServerPerms},
|
||||
handle::{ClientHandler, ClientState},
|
||||
};
|
||||
use clap::Parser;
|
||||
use net::{ClientSender, ConAccepter, listen};
|
||||
use openworm::{
|
||||
net::{
|
||||
ClientMsg, ClientRequestMsg, CreateAccount, CreateAccountResp, DisconnectReason, LoadMsg,
|
||||
Login, LoginResp, RecvHandler, RequestUsersResp, ServerError, ServerMsg, ServerUser,
|
||||
install_crypto_provider,
|
||||
},
|
||||
net::{AccountToken, ClientMsgInst, RecvHandler, install_crypto_provider},
|
||||
rsc::DataDir,
|
||||
};
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
use scrypt::{
|
||||
Scrypt,
|
||||
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString, rand_core::OsRng},
|
||||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
@@ -62,7 +58,7 @@ pub async fn run_server(port: u16) {
|
||||
endpoint.wait_idle().await;
|
||||
}
|
||||
|
||||
pub fn account_token(db: &Db, perms: ServerPerms) -> String {
|
||||
pub fn account_token(db: &Db, perms: ServerPerms) -> AccountToken {
|
||||
let token = Alphanumeric.sample_string(&mut rand::rng(), 16);
|
||||
db.account_tokens.insert(&token, &perms);
|
||||
token
|
||||
@@ -76,14 +72,8 @@ struct ServerListener {
|
||||
count: AtomicU64,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ClientState {
|
||||
Login,
|
||||
Authed(u64),
|
||||
}
|
||||
|
||||
impl ConAccepter for ServerListener {
|
||||
async fn accept(&self, send: ClientSender) -> impl RecvHandler<ClientRequestMsg> {
|
||||
async fn accept(&self, send: ClientSender) -> impl RecvHandler<ClientMsgInst> {
|
||||
let id = self.count.fetch_add(1, Ordering::Release);
|
||||
self.senders.write().await.insert(id, send.clone());
|
||||
ClientHandler {
|
||||
@@ -95,177 +85,3 @@ impl ConAccepter for ServerListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ClientHandler {
|
||||
db: Db,
|
||||
send: ClientSender,
|
||||
senders: Arc<RwLock<HashMap<ClientId, ClientSender>>>,
|
||||
id: ClientId,
|
||||
state: Arc<RwLock<ClientState>>,
|
||||
}
|
||||
|
||||
impl RecvHandler<ClientRequestMsg> for ClientHandler {
|
||||
async fn connect(&self) -> () {}
|
||||
async fn msg(&self, req: ClientRequestMsg) {
|
||||
let msg = ClientMsg::from(req.msg);
|
||||
let replier = self.send.replier(req.id);
|
||||
match msg {
|
||||
ClientMsg::SendMsg(msg) => {
|
||||
let ClientState::Authed(uid) = &*self.state.read().await else {
|
||||
let _ = replier.send(ServerError::NotLoggedIn).await;
|
||||
return;
|
||||
};
|
||||
let msg = Msg {
|
||||
author: *uid,
|
||||
content: msg.content,
|
||||
};
|
||||
// TODO: it is technically possible to send 2 messages at the exact same time...
|
||||
// should probably append a number if one already exists at that time,
|
||||
// but also I can't see this ever happening...?
|
||||
// should be an easy fix later (write tx)
|
||||
let timestamp = time::OffsetDateTime::now_utc().unix_timestamp_nanos();
|
||||
self.db.msgs.insert(×tamp, &msg);
|
||||
let mut handles = Vec::new();
|
||||
let msg = LoadMsg {
|
||||
content: msg.content,
|
||||
author: *uid,
|
||||
};
|
||||
for (&id, send) in self.senders.read().await.iter() {
|
||||
if id == self.id {
|
||||
continue;
|
||||
}
|
||||
let send = send.clone();
|
||||
let msg = msg.clone();
|
||||
let fut = async move {
|
||||
let _ = send
|
||||
.send(LoadMsg {
|
||||
content: msg.content,
|
||||
author: msg.author,
|
||||
})
|
||||
.await;
|
||||
};
|
||||
handles.push(tokio::spawn(fut));
|
||||
}
|
||||
for h in handles {
|
||||
h.await.unwrap();
|
||||
}
|
||||
}
|
||||
ClientMsg::RequestMsgs => {
|
||||
let ClientState::Authed(_uid) = &*self.state.read().await else {
|
||||
let _ = replier.send(ServerError::NotLoggedIn).await;
|
||||
return;
|
||||
};
|
||||
let msgs = self
|
||||
.db
|
||||
.msgs
|
||||
.values()
|
||||
.map(|msg| LoadMsg {
|
||||
content: msg.content,
|
||||
author: msg.author,
|
||||
})
|
||||
.collect();
|
||||
let _ = replier.send(ServerMsg::LoadMsgs(msgs)).await;
|
||||
}
|
||||
ClientMsg::CreateAccount(info) => {
|
||||
let CreateAccount {
|
||||
token,
|
||||
username,
|
||||
password,
|
||||
} = &info;
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let params = scrypt::Params::new(11, 8, 1, 32).unwrap();
|
||||
let hash = Scrypt
|
||||
.hash_password_customized(password.as_bytes(), None, None, params, &salt)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let mut id;
|
||||
loop {
|
||||
let mut tx = self.db.write_tx();
|
||||
let Some(perms) = tx.remove(&self.db.account_tokens, token.to_string()) else {
|
||||
let _ = replier.send(CreateAccountResp::InvalidToken).await;
|
||||
println!("invalid token: {:?}", self.send.remote());
|
||||
return;
|
||||
};
|
||||
if tx.has_key(&self.db.usernames, username.clone()) {
|
||||
let _ = replier.send(CreateAccountResp::UsernameExists).await;
|
||||
return;
|
||||
}
|
||||
id = rand::random();
|
||||
while tx.has_key(&self.db.users, id) {
|
||||
id = rand::random();
|
||||
}
|
||||
tx.insert(
|
||||
&self.db.users,
|
||||
&id,
|
||||
&User {
|
||||
username: username.clone(),
|
||||
password_hash: hash.clone(),
|
||||
bio: String::new(),
|
||||
pfp: None,
|
||||
server_perms: perms,
|
||||
},
|
||||
);
|
||||
tx.insert(&self.db.usernames, username, &id);
|
||||
if tx.commit() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("account created: \"{username}\"");
|
||||
*self.state.write().await = ClientState::Authed(id);
|
||||
let _ = replier.send(CreateAccountResp::Ok { id }).await;
|
||||
}
|
||||
ClientMsg::Login(info) => {
|
||||
let Login { username, password } = &info;
|
||||
let Some(id) = self.db.usernames.get(username) else {
|
||||
let _ = replier.send(LoginResp::UnknownUsername).await;
|
||||
return;
|
||||
};
|
||||
let Some(user) = self.db.users.get(&id) else {
|
||||
panic!("invalid state! (should be a user)");
|
||||
};
|
||||
let hash = PasswordHash::new(&user.password_hash).unwrap();
|
||||
if Scrypt.verify_password(password.as_bytes(), &hash).is_err() {
|
||||
println!("invalid password: \"{username}\"");
|
||||
let _ = replier.send(LoginResp::InvalidPassword).await;
|
||||
return;
|
||||
}
|
||||
*self.state.write().await = ClientState::Authed(id);
|
||||
let _ = replier.send(LoginResp::Ok { id }).await;
|
||||
}
|
||||
ClientMsg::RequestUsers(_) => {
|
||||
if self
|
||||
.db
|
||||
.users
|
||||
.get(&self.id)
|
||||
.is_some_and(|u| !u.server_perms.contains(ServerPerms::ALL))
|
||||
{
|
||||
let _ = replier.send(ServerError::NoPermission).await;
|
||||
return;
|
||||
}
|
||||
let users: Vec<_> = self
|
||||
.db
|
||||
.users
|
||||
.iter()
|
||||
.map(|(id, u)| ServerUser {
|
||||
id,
|
||||
username: u.username,
|
||||
})
|
||||
.collect();
|
||||
let _ = replier.send(RequestUsersResp { users }).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn disconnect(&self, reason: DisconnectReason) -> () {
|
||||
match reason {
|
||||
DisconnectReason::Closed | DisconnectReason::Timeout => (),
|
||||
DisconnectReason::Other(e) => println!("connection issue: {e}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientState {
|
||||
pub fn is_authed(&self) -> bool {
|
||||
matches!(self, Self::Authed(_))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use openworm::net::{
|
||||
ClientRequestMsg, RecvHandler, RequestId, SERVER_NAME, SendResult, ServerMsg, ServerRespMsg,
|
||||
ClientMsgInst, RecvHandler, RequestId, SERVER_NAME, SendResult, ServerMsg, ServerMsgInst,
|
||||
recv_uni, send_uni,
|
||||
};
|
||||
use quinn::{
|
||||
@@ -65,9 +65,9 @@ impl ClientSender {
|
||||
}
|
||||
|
||||
pub async fn send(&self, msg: impl Into<ServerMsg>) -> SendResult {
|
||||
let msg = ServerRespMsg {
|
||||
let msg = ServerMsgInst {
|
||||
id: None,
|
||||
msg: msg.into().into(),
|
||||
msg: msg.into(),
|
||||
};
|
||||
send_uni(&self.conn, msg).await
|
||||
}
|
||||
@@ -79,12 +79,12 @@ pub struct ClientReplier {
|
||||
}
|
||||
|
||||
impl ClientReplier {
|
||||
pub async fn send(&self, msg: impl Into<ServerMsg>) -> SendResult {
|
||||
let msg = ServerRespMsg {
|
||||
pub async fn send(&self, msg: impl Into<ServerMsg>) {
|
||||
let msg = ServerMsgInst {
|
||||
id: Some(self.req_id),
|
||||
msg: msg.into().into(),
|
||||
msg: msg.into(),
|
||||
};
|
||||
send_uni(&self.conn, msg).await
|
||||
let _ = send_uni(&self.conn, msg).await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ pub trait ConAccepter: Send + Sync + 'static {
|
||||
fn accept(
|
||||
&self,
|
||||
send: ClientSender,
|
||||
) -> impl Future<Output = impl RecvHandler<ClientRequestMsg>> + Send;
|
||||
) -> impl Future<Output = impl RecvHandler<ClientMsgInst>> + Send;
|
||||
}
|
||||
|
||||
pub fn listen(
|
||||
|
||||
Reference in New Issue
Block a user