data access interface
This commit is contained in:
		
							parent
							
								
									d0fe80e3cf
								
							
						
					
					
						commit
						2fb97cc2b6
					
				| @ -61,6 +61,7 @@ impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> { | ||||
| 
 | ||||
|     // TODO: this may need to pad to 8 bytes for bb cipher
 | ||||
|     fn send(&mut self, pkt: &dyn PSOPacket) { | ||||
|         println!("[send] {:?}", pkt); | ||||
|         let buf = pkt.as_bytes(); | ||||
|         let mut cbuf = self.cipher_out.encrypt(&buf).unwrap(); | ||||
|         self.send_buffer.append(&mut cbuf); | ||||
| @ -90,6 +91,7 @@ impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> { | ||||
| 
 | ||||
|                             match pkt { | ||||
|                                 Ok(pkt) => { | ||||
|                                     println!("[recv] {:?}", pkt); | ||||
|                                     let response = self.state.handle(&pkt); | ||||
|                                     for r in response { | ||||
|                                         self.send(&*r); | ||||
| @ -98,7 +100,7 @@ impl<P: ServerPacket + std::fmt::Debug, E: std::fmt::Debug> Client<P, E> { | ||||
|                                 Err(err) => { | ||||
|                                     match err { | ||||
|                                         PacketNetworkError::ClientDisconnected => self.running = false, | ||||
|                                         _ => println!("error recv-ing packet with {:?}: {:?}", self.socket, err), | ||||
|                                         _ => {} //println!("error recv-ing packet with {:?}: {:?}", self.socket, err),
 | ||||
|                                     } | ||||
|                                     break; | ||||
|                                 } | ||||
|  | ||||
| @ -3,11 +3,6 @@ use std::net; | ||||
| use rand::{Rng, RngCore}; | ||||
| use bcrypt::{DEFAULT_COST, hash, verify}; | ||||
| 
 | ||||
| use diesel::r2d2; | ||||
| use diesel::prelude::*; | ||||
| use diesel::pg::PgConnection; | ||||
| use elseware::schema::*; | ||||
| 
 | ||||
| use libpso::packet::login::*; | ||||
| use libpso::{PacketParseError, PSOPacket}; | ||||
| use libpso::crypto::{CipherError, PSOCipher, NullCipher}; | ||||
| @ -21,11 +16,11 @@ use elseware::common::client::Client; | ||||
| use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect}; | ||||
| use elseware::common::util::array_to_utf8; | ||||
| 
 | ||||
| use crate::dataaccess::DataAccess; | ||||
| use crate::login::{SharedLoginState, get_login_status}; | ||||
| use crate::models::UserAccount; | ||||
| 
 | ||||
| pub const CHARACTER_PORT: u16 = 12001; | ||||
| type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>; | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum CharacterError { | ||||
| @ -47,13 +42,13 @@ impl ServerPacket for CharacterPacket { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub struct CharacterServerState { | ||||
|     shared_state: SharedLoginState, | ||||
| pub struct CharacterServerState<DA: DataAccess> { | ||||
|     shared_state: SharedLoginState<DA>, | ||||
|     user: Option<UserAccount>, | ||||
| } | ||||
| 
 | ||||
| impl CharacterServerState { | ||||
|     fn new(shared_state: SharedLoginState) -> CharacterServerState { | ||||
| impl<DA: DataAccess> CharacterServerState<DA> { | ||||
|     fn new(shared_state: SharedLoginState<DA>) -> CharacterServerState<DA> { | ||||
|         CharacterServerState { | ||||
|             shared_state: shared_state, | ||||
|             user: None, | ||||
| @ -61,7 +56,7 @@ impl CharacterServerState { | ||||
|     } | ||||
| 
 | ||||
|     fn validate_login(&mut self, pkt: &Login) -> Box<PktVec> { | ||||
|         match get_login_status(&self.shared_state.connection_pool, pkt) { | ||||
|         match get_login_status(&self.shared_state.data_access, pkt) { | ||||
|             Ok(user) => { | ||||
|                 let mut response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data); | ||||
|                 response.guildcard = user.guildcard.map_or(0, |gc| gc) as u32; | ||||
| @ -80,7 +75,7 @@ impl CharacterServerState { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ServerState for CharacterServerState { | ||||
| impl<DA: DataAccess> ServerState for CharacterServerState<DA> { | ||||
|     type Packet = CharacterPacket; | ||||
|     type PacketError = CharacterError; | ||||
| 
 | ||||
| @ -107,7 +102,6 @@ impl ServerState for CharacterServerState { | ||||
|             CharacterPacket::RequestSettings(_req) => { | ||||
|                 self.get_settings() | ||||
|             } | ||||
|             
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -116,7 +110,7 @@ impl ServerState for CharacterServerState { | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| pub fn new_client(socket: mio::tcp::TcpStream, shared_state: SharedLoginState) { | ||||
| pub fn new_client<DA: DataAccess + 'static>(socket: mio::tcp::TcpStream, shared_state: SharedLoginState<DA>) { | ||||
|     let state = CharacterServerState::new(shared_state); | ||||
|     let client = Client::new(socket, Box::new(state)); | ||||
|     client.io_loop(); | ||||
|  | ||||
							
								
								
									
										48
									
								
								src/login/dataaccess.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/login/dataaccess.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| use diesel::r2d2; | ||||
| use diesel::prelude::*; | ||||
| use diesel::pg::PgConnection; | ||||
| 
 | ||||
| use crate::models::*; | ||||
| 
 | ||||
| type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>; | ||||
| 
 | ||||
| pub trait DataAccess { | ||||
|     fn get_user_by_id(&self, id: u32) -> Option<UserAccount> { | ||||
|         unimplemented!(); | ||||
|     } | ||||
| 
 | ||||
|     fn get_user_by_name(&self, username: String) -> Option<UserAccount> { | ||||
|         unimplemented!(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct DBAccess { | ||||
|     connection_pool: ConnectionPool, | ||||
| } | ||||
| 
 | ||||
| impl DBAccess { | ||||
|     pub fn new(pool: ConnectionPool) -> DBAccess { | ||||
|         DBAccess { | ||||
|             connection_pool: pool, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| impl DataAccess for DBAccess { | ||||
|     fn get_user_by_name(&self, name: String) -> Option<UserAccount> { | ||||
|         use elseware::schema::user_accounts::dsl::{user_accounts, username}; | ||||
|         self.connection_pool.get() | ||||
|             .map(|conn| { | ||||
|                 user_accounts.filter(username.eq(name)).load::<UserAccount>(&conn) | ||||
|                     .map(|mut user| user.pop()).unwrap_or(None) | ||||
|             }) | ||||
|             .unwrap_or(None) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*impl DataAccess for DatabaseAccess {
 | ||||
| }*/ | ||||
| 
 | ||||
| @ -1,13 +1,10 @@ | ||||
| // TODO: rename this module to auth
 | ||||
| 
 | ||||
| use std::net; | ||||
| 
 | ||||
| use rand::{Rng, RngCore}; | ||||
| use bcrypt::{DEFAULT_COST, hash, verify}; | ||||
| 
 | ||||
| use diesel::r2d2; | ||||
| use diesel::prelude::*; | ||||
| use diesel::pg::PgConnection; | ||||
| use elseware::schema::*; | ||||
| 
 | ||||
| use libpso::packet::login::*; | ||||
| use libpso::{PacketParseError, PSOPacket}; | ||||
| use libpso::crypto::{CipherError, PSOCipher, NullCipher}; | ||||
| @ -16,15 +13,16 @@ use libpso::crypto::bb::PSOBBCipher; | ||||
| use elseware::pktvec; | ||||
| use elseware::common::pktvec::PktVec; | ||||
| use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; | ||||
| use elseware::common::network::{PacketNetworkError}; | ||||
| //use elseware::common::network::{PacketNetworkError};
 | ||||
| use elseware::common::client::Client; | ||||
| use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect}; | ||||
| use elseware::common::util::array_to_utf8; | ||||
| 
 | ||||
| use crate::dataaccess::DataAccess; | ||||
| use crate::models::UserAccount; | ||||
| 
 | ||||
| pub const LOGIN_PORT: u16 = 12000; | ||||
| type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>; | ||||
| //type ConnectionPool = r2d2::Pool<r2d2::ConnectionManager<PgConnection>>;
 | ||||
| 
 | ||||
| #[derive(Debug)] | ||||
| pub enum LoginError { | ||||
| @ -46,78 +44,45 @@ impl ServerPacket for LoginPacket { | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone)] | ||||
| pub struct SharedLoginState { | ||||
|     pub connection_pool: ConnectionPool, | ||||
| pub struct SharedLoginState<DA: DataAccess> { | ||||
|     pub data_access: DA, | ||||
| } | ||||
| 
 | ||||
| impl SharedLoginState { | ||||
|     pub fn new(pool: ConnectionPool) -> SharedLoginState { | ||||
| impl<DA: DataAccess> SharedLoginState<DA> { | ||||
|     pub fn new(data_access: DA) -> SharedLoginState<DA> { | ||||
|         SharedLoginState { | ||||
|             connection_pool: pool, | ||||
|             data_access: data_access, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| pub struct LoginServerState { | ||||
|     pub shared_state: SharedLoginState, | ||||
| pub struct LoginServerState<DA: DataAccess> { | ||||
|     pub shared_state: SharedLoginState<DA>, | ||||
| } | ||||
| 
 | ||||
| pub fn get_login_status(connection_pool: &ConnectionPool, pkt: &Login) -> Result<UserAccount, AccountStatus> { | ||||
|     use elseware::schema::user_accounts::dsl::{user_accounts, username}; | ||||
|     connection_pool.get() | ||||
|         .map_err(|_err| AccountStatus::Error) | ||||
|         .and_then(|conn| { | ||||
|             array_to_utf8(pkt.username) | ||||
|                 .map(|username_str| { | ||||
|                     (conn, username_str) | ||||
|                 }) | ||||
|                 .map_err(|err| { | ||||
|                     println!("utf err: {:?}", err); | ||||
|                     AccountStatus::Error | ||||
|                 }) | ||||
|         }) | ||||
|         .and_then(|(conn, username_str)| { | ||||
|             user_accounts.filter(username.eq(username_str)).load::<UserAccount>(&conn) | ||||
|                 .map_err(|err| { | ||||
|                     println!("sql err: {:?}", err); | ||||
|                     AccountStatus::Error | ||||
|                 }) | ||||
|         }) | ||||
|         .and_then(|mut result| { | ||||
|             result.pop().ok_or(AccountStatus::InvalidUser) | ||||
|         }) | ||||
|         .and_then(|user| { | ||||
|             array_to_utf8(pkt.password) | ||||
|                 .map(|password| { | ||||
|                     (user, password) | ||||
|                 }) | ||||
|                 .map_err(|_err| AccountStatus::Error) | ||||
|         }) | ||||
|         .and_then(|(user, password)| { | ||||
|             bcrypt::verify(password, user.password.as_str()) | ||||
|                 .map_err(|err| { | ||||
|                     println!("bcrypt err: {:?}", err); | ||||
|                     AccountStatus::Error | ||||
|                 }) | ||||
|                 .and_then(|correct_password| { | ||||
|                     match correct_password { | ||||
|                         true => Ok(user), | ||||
|                         false => Err(AccountStatus::InvalidPassword), | ||||
|                     } | ||||
|                 }) | ||||
|         }) | ||||
| 
 | ||||
| pub fn get_login_status(data_access: &dyn DataAccess, pkt: &Login) -> Result<UserAccount, AccountStatus> { | ||||
|     let username = array_to_utf8(pkt.username).map_err(|_err| AccountStatus::Error)?; | ||||
|     let password = array_to_utf8(pkt.password).map_err(|_err| AccountStatus::Error)?; | ||||
|     let user = data_access.get_user_by_name(username).ok_or(AccountStatus::InvalidUser)?; | ||||
|     let verified = bcrypt::verify(password, user.password.as_str()).map_err(|_err| AccountStatus::Error)?; | ||||
|     match verified { | ||||
|         true => Ok(user), | ||||
|         false => Err(AccountStatus::InvalidPassword) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl LoginServerState { | ||||
|     fn new(shared_state: SharedLoginState) -> LoginServerState { | ||||
| 
 | ||||
| impl<DA: DataAccess> LoginServerState<DA> { | ||||
|     fn new(shared_state: SharedLoginState<DA>) -> LoginServerState<DA> { | ||||
|         LoginServerState { | ||||
|             shared_state: shared_state, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn validate_login(&mut self, pkt: &Login) -> Box<PktVec> { | ||||
|         match get_login_status(&self.shared_state.connection_pool, pkt) { | ||||
|         match get_login_status(&self.shared_state.data_access, pkt) { | ||||
|             Ok(_user) => { | ||||
|                 let response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data); | ||||
|                 let ip = net::Ipv4Addr::new(127,0,0,1); | ||||
| @ -131,7 +96,7 @@ impl LoginServerState { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl ServerState for LoginServerState { | ||||
| impl<DA: DataAccess> ServerState for LoginServerState<DA> { | ||||
|     type Packet = LoginPacket; | ||||
|     type PacketError = LoginError; | ||||
| 
 | ||||
| @ -150,7 +115,6 @@ impl ServerState for LoginServerState { | ||||
|     } | ||||
| 
 | ||||
|     fn handle(&mut self, pkt: &LoginPacket) -> Box<dyn Iterator<Item = Box<dyn PSOPacket>>> { | ||||
|         println!("[login: recv] {:?}", pkt); | ||||
|         match pkt { | ||||
|             LoginPacket::Login(login) => { | ||||
|                 self.validate_login(login) | ||||
| @ -160,9 +124,7 @@ impl ServerState for LoginServerState { | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| pub fn new_client(socket: mio::tcp::TcpStream, shared_state: SharedLoginState) { | ||||
| pub fn new_client<DA: DataAccess + 'static>(socket: mio::tcp::TcpStream, shared_state: SharedLoginState<DA>) { | ||||
|     let state = LoginServerState::new(shared_state); | ||||
|     let client = Client::new(socket, Box::new(state)); | ||||
|     client.io_loop(); | ||||
|  | ||||
| @ -1,9 +1,12 @@ | ||||
| #![feature(const_generics)] | ||||
| 
 | ||||
| mod login; | ||||
| mod character; | ||||
| mod dataaccess; | ||||
| mod models; | ||||
| 
 | ||||
| use std::net::{SocketAddr, Ipv4Addr}; | ||||
| use std::net; | ||||
| //use std::net;
 | ||||
| use std::thread; | ||||
| use std::env; | ||||
| 
 | ||||
| @ -12,28 +15,50 @@ use mio::{Events, Poll, Token, Ready, PollOpt}; | ||||
| use dotenv::dotenv; | ||||
| 
 | ||||
| use diesel::r2d2; | ||||
| use diesel::prelude::*; | ||||
| //use diesel::prelude::*;
 | ||||
| use diesel::pg::PgConnection; | ||||
| 
 | ||||
| use models::{NewUser, UserAccount}; | ||||
| //use models::{NewUser, UserAccount, UserSettings, NewUserSettings};
 | ||||
| use dataaccess::DBAccess; | ||||
| 
 | ||||
| fn main() { | ||||
|     dotenv().ok(); | ||||
|     let database_url = env::var("DATABASE_URL").unwrap(); | ||||
| 
 | ||||
|     /*if let Some(arg) = env::args().nth(1) {
 | ||||
|         if arg == "dbstuff" { | ||||
|             let conn = PgConnection::establish(&database_url).unwrap(); | ||||
| 
 | ||||
|             use elseware::schema::user_accounts::dsl::*; | ||||
|             use elseware::schema::user_settings::dsl::*; | ||||
| 
 | ||||
|             let u = NewUser::new("hi".to_string(), "qwer".to_string()); | ||||
|             diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap(); | ||||
|             let u = NewUser::new("hi2".to_string(), "qwer".to_string()); | ||||
|             let user: UserAccount = diesel::insert_into(user_accounts).values(&u).get_result(&conn).unwrap(); | ||||
| 
 | ||||
|             let mut s = models::EUserSettings(libpso::character::settings::UserSettings::default()); | ||||
|             s.0.blocked_users[5] = 99; | ||||
|             s.0.blocked_users[6] = 123; | ||||
| 
 | ||||
|             diesel::insert_into(user_settings).values(& NewUserSettings { | ||||
|                 user_id: user.id, | ||||
|                 settings: s, | ||||
|             }).execute(&conn).unwrap(); | ||||
| 
 | ||||
|             let us = user_settings.load::<UserSettings>(&conn).unwrap(); | ||||
|             for u in us { | ||||
|                 println!("{:?}", u.settings.0.blocked_users[4]); | ||||
|                 println!("{:?}", u.settings.0.blocked_users[5]); | ||||
|                 println!("{:?}", u.settings.0.blocked_users[6]); | ||||
|             } | ||||
|         } | ||||
|     }*/ | ||||
| 
 | ||||
|     //let database_url = env::var("DATABASE_URL").unwrap();
 | ||||
|     //let conn = PgConnection::establish(&database_url).unwrap();
 | ||||
| 
 | ||||
|     //use elseware::schema::user_accounts::dsl::*;
 | ||||
|     //use elseware::schema::user_accounts::table;
 | ||||
| 
 | ||||
|     /*let u = NewUser::new("hi".to_string(), "qwer".to_string());
 | ||||
|     diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap(); | ||||
|     let u = NewUser::new("hi2".to_string(), "qwer".to_string()); | ||||
|     diesel::insert_into(user_accounts).values(&u).execute(&conn).unwrap();*/ | ||||
| 
 | ||||
|     println!("[login+character] starting server"); | ||||
| 
 | ||||
|     let database_url = env::var("DATABASE_URL").unwrap(); | ||||
|     let connection_manager = r2d2::ConnectionManager::<PgConnection>::new(database_url); | ||||
|     let connection_pool = r2d2::Pool::builder() | ||||
|         .build(connection_manager).unwrap(); | ||||
| @ -41,7 +66,7 @@ fn main() { | ||||
|     let login_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), login::LOGIN_PORT))).unwrap(); | ||||
|     let character_listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), character::CHARACTER_PORT))).unwrap(); | ||||
| 
 | ||||
|     let login_shared_state = login::SharedLoginState::new(connection_pool); | ||||
|     let login_shared_state = login::SharedLoginState::new(DBAccess::new(connection_pool)); | ||||
| 
 | ||||
|     let poll = Poll::new().unwrap(); | ||||
|     poll.register(&login_listener, Token(0), Ready::readable(), PollOpt::edge()).unwrap(); | ||||
|  | ||||
| @ -1,8 +0,0 @@ | ||||
| #![feature(async_await)] | ||||
| 
 | ||||
| mod common; | ||||
| //mod patch;
 | ||||
| 
 | ||||
| fn main() { | ||||
|     // something or other?
 | ||||
| } | ||||
| @ -9,6 +9,7 @@ use std::path::{Path, PathBuf, Components}; | ||||
| use std::convert::AsRef; | ||||
| use rand::{Rng, RngCore}; | ||||
| use crc::{crc32, Hasher32}; | ||||
| use mio::tcp::TcpStream; | ||||
| use libpso::{PacketParseError, PSOPacket}; | ||||
| use libpso::packet::patch::*; | ||||
| use libpso::crypto::pc::PSOPCCipher; | ||||
| @ -348,7 +349,7 @@ impl Iterator for SendFileIterator { | ||||
| 
 | ||||
| fn new_client(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file_lookup: HashMap<u32, PatchFile>) { | ||||
|     let state = PatchServerState::new(patch_file_tree, patch_file_lookup); | ||||
|     let client = Client::new(socket, Box::new(state)); | ||||
|     let client = Client::new(TcpStream::from_stream(socket).unwrap(), Box::new(state)); | ||||
|     client.io_loop(); | ||||
| } | ||||
| 
 | ||||
| @ -357,8 +358,6 @@ fn new_client(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file | ||||
| fn main() { | ||||
|     println!("[patch] starting server"); | ||||
| 
 | ||||
|     let listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), PATCH_PORT))).unwrap(); | ||||
| 
 | ||||
|     let (patch_file_tree, patch_file_lookup) = generate_patch_tree("patchfiles/"); | ||||
|     println!("[patch] files to patch:"); | ||||
|     let mut indent = 0; | ||||
| @ -379,6 +378,8 @@ fn main() { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     let listener = TcpListener::bind(&SocketAddr::from((Ipv4Addr::new(0,0,0,0), PATCH_PORT))).unwrap(); | ||||
|     
 | ||||
|     println!("[patch] waiting for connections"); | ||||
|     while let Ok((socket, addr)) = listener.accept() { | ||||
|         let local_patch_file_tree = patch_file_tree.clone(); | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user