diff --git a/planetwars-server/Cargo.toml b/planetwars-server/Cargo.toml index f2444c1..2b3916c 100644 --- a/planetwars-server/Cargo.toml +++ b/planetwars-server/Cargo.toml @@ -2,8 +2,16 @@ name = "planetwars-server" version = "0.0.0" edition = "2021" +default-run = "planetwars-server" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "planetwars-server" +path = "src/main.rs" + +[[bin]] +name = "planetwars-server-cli" +path = "src/cli.rs" [dependencies] futures = "0.3" @@ -32,6 +40,7 @@ sha2 = "0.10" tokio-util = { version="0.7.3", features=["io"] } prost = "0.10" tonic = "0.7.2" +clap = { version = "3.2", features = ["derive", "env"]} # TODO: remove me shlex = "1.1" diff --git a/planetwars-server/src/cli.rs b/planetwars-server/src/cli.rs new file mode 100644 index 0000000..f33506e --- /dev/null +++ b/planetwars-server/src/cli.rs @@ -0,0 +1,54 @@ +extern crate planetwars_server; +extern crate tokio; + +use clap::Parser; +use planetwars_server::db; +use planetwars_server::{create_db_pool, get_config}; + +#[derive(clap::Parser)] +struct Args { + #[clap(subcommand)] + action: Action, +} + +#[derive(clap::Subcommand)] +enum Action { + SetPassword(SetPassword), +} + +impl Action { + async fn run(self) { + match self { + Action::SetPassword(set_password) => set_password.run().await, + } + } +} + +#[derive(clap::Parser)] +struct SetPassword { + #[clap(value_parser)] + username: String, + + #[clap(value_parser)] + new_password: String, +} + +impl SetPassword { + async fn run(self) { + let global_config = get_config().unwrap(); + let pool = create_db_pool(&global_config).await; + + let conn = pool.get().await.expect("could not get database connection"); + let credentials = db::users::Credentials { + username: &self.username, + password: &self.new_password, + }; + db::users::set_user_password(credentials, &conn).expect("could not set password"); + } +} + +#[tokio::main] +pub async fn main() { + let args = Args::parse(); + args.action.run().await; +} diff --git a/planetwars-server/src/db/users.rs b/planetwars-server/src/db/users.rs index ebb2268..9676dae 100644 --- a/planetwars-server/src/db/users.rs +++ b/planetwars-server/src/db/users.rs @@ -42,11 +42,17 @@ fn argon2_config() -> argon2::Config<'static> { } } -pub fn create_user(credentials: &Credentials, conn: &PgConnection) -> QueryResult { +pub fn hash_password(password: &str) -> (Vec, [u8; 32]) { let argon_config = argon2_config(); - let salt: [u8; 32] = rand::thread_rng().gen(); - let hash = argon2::hash_raw(credentials.password.as_bytes(), &salt, &argon_config).unwrap(); + let hash = argon2::hash_raw(password.as_bytes(), &salt, &argon_config).unwrap(); + + (hash, salt) +} + +pub fn create_user(credentials: &Credentials, conn: &PgConnection) -> QueryResult { + let (hash, salt) = hash_password(&credentials.password); + let new_user = NewUser { username: credentials.username, password_salt: &salt, @@ -69,6 +75,22 @@ pub fn find_user_by_name(username: &str, db_conn: &PgConnection) -> QueryResult< .first::(db_conn) } +pub fn set_user_password(credentials: Credentials, db_conn: &PgConnection) -> QueryResult<()> { + let (hash, salt) = hash_password(&credentials.password); + + let n_changes = diesel::update(users::table.filter(users::username.eq(&credentials.username))) + .set(( + users::password_salt.eq(salt.as_slice()), + users::password_hash.eq(hash.as_slice()), + )) + .execute(db_conn)?; + if n_changes == 0 { + Err(diesel::result::Error::NotFound) + } else { + Ok(()) + } +} + pub fn authenticate_user(credentials: &Credentials, db_conn: &PgConnection) -> Option { find_user_by_name(credentials.username, db_conn) .optional() diff --git a/planetwars-server/src/lib.rs b/planetwars-server/src/lib.rs index 804dcd5..4950815 100644 --- a/planetwars-server/src/lib.rs +++ b/planetwars-server/src/lib.rs @@ -94,11 +94,9 @@ pub async fn seed_simplebot(config: &GlobalConfig, pool: &ConnectionPool) { pub type DbPool = Pool>; -pub async fn prepare_db(config: &GlobalConfig) -> DbPool { +pub async fn create_db_pool(config: &GlobalConfig) -> DbPool { let manager = DieselConnectionManager::::new(&config.database_url); - let pool = bb8::Pool::builder().build(manager).await.unwrap(); - seed_simplebot(config, &pool).await; - pool + bb8::Pool::builder().build(manager).await.unwrap() } // create all directories required for further operation @@ -165,7 +163,8 @@ async fn run_registry(config: Arc, db_pool: DbPool) { pub async fn run_app() { let global_config = Arc::new(get_config().unwrap()); - let db_pool = prepare_db(&global_config).await; + let db_pool = create_db_pool(&global_config).await; + seed_simplebot(&global_config, &db_pool).await; init_directories(&global_config).unwrap(); if global_config.ranker_enabled {