-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Elizabeth Myers <[email protected]>
- Loading branch information
Showing
12 changed files
with
519 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* SPDX-License-Identifier: CC0-1.0 | ||
* | ||
* src/cli.rs | ||
* | ||
* This file is a component of ShadyURL by Elizabeth Myers. | ||
* | ||
* To the extent possible under law, the person who associated CC0 with | ||
* ShadyURL has waived all copyright and related or neighboring rights | ||
* to ShadyURL. | ||
* | ||
* You should have received a copy of the CC0 legalcode along with this | ||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
*/ | ||
|
||
pub mod parser; | ||
mod subcommands; | ||
|
||
pub use parser::run_command; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* SPDX-License-Identifier: CC0-1.0 | ||
* | ||
* src/cli/parser.rs | ||
* | ||
* This file is a component of ShadyURL by Elizabeth Myers. | ||
* | ||
* To the extent possible under law, the person who associated CC0 with | ||
* ShadyURL has waived all copyright and related or neighboring rights | ||
* to ShadyURL. | ||
* | ||
* You should have received a copy of the CC0 legalcode along with this | ||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
*/ | ||
|
||
use clap::{Args, Parser, Subcommand}; | ||
|
||
use crate::cli::subcommands::{ | ||
AddUserSubcommand, ChangePasswordSubcommand, CliSubcommand, DeleteUserSubcommand, | ||
GenerateKeysSubcommand, RunSubcommand, | ||
}; | ||
|
||
// Simple things | ||
#[derive(Debug, Clone, Args)] | ||
pub struct UsernameArgument { | ||
pub username: String, | ||
} | ||
|
||
#[derive(Parser)] | ||
struct Cli { | ||
#[command(subcommand)] | ||
command: Option<Commands>, | ||
} | ||
|
||
#[derive(Subcommand)] | ||
pub enum Commands { | ||
Run, | ||
AddUser(UsernameArgument), | ||
DeleteUser(UsernameArgument), | ||
ChangePassword(UsernameArgument), | ||
GenerateKeys, | ||
} | ||
|
||
pub async fn run_command() -> Result<(), Box<dyn std::error::Error>> { | ||
let cli = Cli::parse(); | ||
match &cli.command { | ||
Some(Commands::AddUser(data)) => { | ||
AddUserSubcommand::run_command(data).await?; | ||
Ok(()) | ||
} | ||
Some(Commands::DeleteUser(data)) => { | ||
DeleteUserSubcommand::run_command(data).await?; | ||
Ok(()) | ||
} | ||
Some(Commands::ChangePassword(data)) => { | ||
ChangePasswordSubcommand::run_command(data).await?; | ||
Ok(()) | ||
} | ||
Some(Commands::GenerateKeys) => { | ||
GenerateKeysSubcommand::run_command(&()).await?; | ||
Ok(()) | ||
} | ||
Some(Commands::Run) | None => { | ||
RunSubcommand::run_command(&()).await?; | ||
Ok(()) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
/* SPDX-License-Identifier: CC0-1.0 | ||
* src/cli/subcommands.rs | ||
* | ||
* This file is a component of ShadyURL by Elizabeth Myers. | ||
* | ||
* To the extent possible under law, the person who associated CC0 with | ||
* ShadyURL has waived all copyright and related or neighboring rights | ||
* to ShadyURL. | ||
* | ||
* You should have received a copy of the CC0 legalcode along with this | ||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
*/ | ||
|
||
mod adduser; | ||
mod changepassword; | ||
mod deleteuser; | ||
mod generatekeys; | ||
mod run; | ||
|
||
use proctitle::set_title; | ||
|
||
use crate::env::{EnvError, Vars}; | ||
|
||
#[async_trait::async_trait] | ||
pub trait CliSubcommand { | ||
type Error; | ||
type PromptUserData: Send + Sync; | ||
type CommandData: Send + Sync; | ||
|
||
fn proc_title() -> String { | ||
"shadyurl-rust [command]".to_string() | ||
} | ||
|
||
fn load_env() -> Result<Vars, EnvError> { | ||
Vars::load_env() | ||
} | ||
|
||
fn prompt_user() -> Result<Self::PromptUserData, Self::Error>; | ||
|
||
fn check_cli_args(_: Self::CommandData) -> Result<(), Self::Error> { | ||
Ok(()) | ||
} | ||
|
||
async fn run_command(data: &Self::CommandData) -> Result<(), Self::Error> { | ||
set_title(Self::proc_title()); | ||
let env = Self::load_env().expect("Could not load env"); | ||
let prompt = Self::prompt_user()?; | ||
Self::run(env, prompt, data).await | ||
} | ||
|
||
async fn run( | ||
env: Vars, | ||
prompt: Self::PromptUserData, | ||
data: &Self::CommandData, | ||
) -> Result<(), Self::Error>; | ||
} | ||
|
||
pub use adduser::AddUserSubcommand; | ||
pub use changepassword::ChangePasswordSubcommand; | ||
pub use deleteuser::DeleteUserSubcommand; | ||
pub use generatekeys::GenerateKeysSubcommand; | ||
pub use run::RunSubcommand; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* SPDX-License-Identifier: CC0-1.0 | ||
* | ||
* src/cli/subcommands/adduser.rs | ||
* | ||
* This file is a component of ShadyURL by Elizabeth Myers. | ||
* | ||
* To the extent possible under law, the person who associated CC0 with | ||
* ShadyURL has waived all copyright and related or neighboring rights | ||
* to ShadyURL. | ||
* | ||
* You should have received a copy of the CC0 legalcode along with this | ||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
*/ | ||
|
||
use password_auth::generate_hash; | ||
use rpassword::prompt_password; | ||
|
||
use crate::{ | ||
cli::{parser::UsernameArgument, subcommands::CliSubcommand}, | ||
env::{EnvError, Vars}, | ||
}; | ||
|
||
use service::{Database, Mutation}; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum CliError { | ||
#[error(transparent)] | ||
Db(#[from] sea_orm::DbErr), | ||
|
||
#[error(transparent)] | ||
Io(#[from] std::io::Error), | ||
|
||
#[error(transparent)] | ||
Env(#[from] EnvError), | ||
|
||
#[error("Passwords did not match")] | ||
PasswordMismatch, | ||
} | ||
|
||
pub struct AddUserData { | ||
pub password: String, | ||
} | ||
|
||
pub struct AddUserSubcommand; | ||
|
||
#[async_trait::async_trait] | ||
impl CliSubcommand for AddUserSubcommand { | ||
type Error = CliError; | ||
type PromptUserData = AddUserData; | ||
type CommandData = UsernameArgument; | ||
|
||
fn prompt_user() -> Result<Self::PromptUserData, Self::Error> { | ||
let mut password = prompt_password("Password:")?; | ||
if password != prompt_password("Repeat password:")? { | ||
return Err(CliError::PasswordMismatch); | ||
} | ||
|
||
password = generate_hash(password); | ||
|
||
Ok(Self::PromptUserData { password }) | ||
} | ||
|
||
async fn run( | ||
env: Vars, | ||
prompt: Self::PromptUserData, | ||
data: &Self::CommandData, | ||
) -> Result<(), Self::Error> { | ||
let db = Database::get(&env.database_url).await?; | ||
Mutation::create_user(&db, &data.username, &prompt.password).await?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/* SPDX-License-Identifier: CC0-1.0 | ||
* | ||
* src/cli/subcommands/changepassword.rs | ||
* | ||
* This file is a component of ShadyURL by Elizabeth Myers. | ||
* | ||
* To the extent possible under law, the person who associated CC0 with | ||
* ShadyURL has waived all copyright and related or neighboring rights | ||
* to ShadyURL. | ||
* | ||
* You should have received a copy of the CC0 legalcode along with this | ||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
*/ | ||
|
||
use password_auth::generate_hash; | ||
use rpassword::prompt_password; | ||
|
||
use crate::{ | ||
cli::{parser::UsernameArgument, subcommands::CliSubcommand}, | ||
env::{EnvError, Vars}, | ||
}; | ||
|
||
use service::{Database, Mutation}; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum CliError { | ||
#[error(transparent)] | ||
Db(#[from] sea_orm::DbErr), | ||
|
||
#[error(transparent)] | ||
Io(#[from] std::io::Error), | ||
|
||
#[error(transparent)] | ||
Env(#[from] EnvError), | ||
|
||
#[error("Passwords did not match")] | ||
PasswordMismatch, | ||
} | ||
|
||
pub struct ChangePasswordData { | ||
password: String, | ||
} | ||
|
||
pub struct ChangePasswordSubcommand; | ||
|
||
#[async_trait::async_trait] | ||
impl CliSubcommand for ChangePasswordSubcommand { | ||
type Error = CliError; | ||
type PromptUserData = ChangePasswordData; | ||
type CommandData = UsernameArgument; | ||
|
||
fn prompt_user() -> Result<Self::PromptUserData, Self::Error> { | ||
let mut password = prompt_password("Password:")?; | ||
if password != prompt_password("Repeat password:")? { | ||
return Err(CliError::PasswordMismatch); | ||
} | ||
|
||
password = generate_hash(password); | ||
|
||
Ok(Self::PromptUserData { password }) | ||
} | ||
|
||
async fn run( | ||
env: Vars, | ||
prompt: Self::PromptUserData, | ||
data: &Self::CommandData, | ||
) -> Result<(), Self::Error> { | ||
let db = Database::get(&env.database_url).await?; | ||
Mutation::change_user_password(&db, &data.username, &prompt.password).await?; | ||
Ok(()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
/* SPDX-License-Identifier: CC0-1.0 | ||
* | ||
* src/cli/subcommands/deleteuser.rs | ||
* | ||
* This file is a component of ShadyURL by Elizabeth Myers. | ||
* | ||
* To the extent possible under law, the person who associated CC0 with | ||
* ShadyURL has waived all copyright and related or neighboring rights | ||
* to ShadyURL. | ||
* | ||
* You should have received a copy of the CC0 legalcode along with this | ||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>. | ||
*/ | ||
|
||
use std::io::{prelude::*, stdin, stdout}; | ||
|
||
use crate::{ | ||
cli::{parser::UsernameArgument, subcommands::CliSubcommand}, | ||
env::{EnvError, Vars}, | ||
}; | ||
|
||
use service::{Database, Mutation, Query}; | ||
|
||
#[derive(Debug, thiserror::Error)] | ||
pub enum CliError { | ||
#[error(transparent)] | ||
Db(#[from] sea_orm::DbErr), | ||
|
||
#[error(transparent)] | ||
Io(#[from] std::io::Error), | ||
|
||
#[error(transparent)] | ||
Env(#[from] EnvError), | ||
|
||
#[error("Aborted")] | ||
Aborted, | ||
|
||
#[error("User not found")] | ||
NotFound, | ||
} | ||
|
||
pub struct DeleteUserSubcommand; | ||
|
||
#[async_trait::async_trait] | ||
impl CliSubcommand for DeleteUserSubcommand { | ||
type Error = CliError; | ||
type PromptUserData = (); | ||
type CommandData = UsernameArgument; | ||
|
||
fn prompt_user() -> Result<Self::PromptUserData, Self::Error> { | ||
let mut response = String::new(); | ||
loop { | ||
print!("Are you SURE you want to delete this user? [yes/no] "); | ||
stdout().flush()?; | ||
stdin().lock().read_line(&mut response)?; | ||
response = response.trim_end().to_ascii_lowercase().to_string(); | ||
match response.as_str() { | ||
"no" | "n" => return Err(CliError::Aborted), | ||
"yes" => break, | ||
_ => { | ||
response.clear(); | ||
println!("Please type yes or no."); | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
async fn run( | ||
env: Vars, | ||
_: Self::PromptUserData, | ||
data: &Self::CommandData, | ||
) -> Result<(), Self::Error> { | ||
let db = Database::get(&env.database_url).await?; | ||
let user = Query::find_user_by_username(&db, &data.username) | ||
.await? | ||
.ok_or(CliError::NotFound)?; | ||
|
||
Mutation::delete_user(&db, user.id).await?; | ||
|
||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.