authentication overhaul

This commit is contained in:
Wyatt J. Miller 2021-07-27 01:48:05 -04:00
parent 22c1abd093
commit ba11df89c8
6 changed files with 120 additions and 43 deletions

View File

@ -2,5 +2,6 @@
"api_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "api_token": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"base_api": "/api/v1", "base_api": "/api/v1",
"base_url": "https://www.example.com", "base_url": "https://www.example.com",
"username": "jimmyjoebob" "username": "jimmyjoebob",
"password": "ihazcheeseburgerz"
} }

View File

@ -18,7 +18,7 @@ pub fn get_args() -> ArgMatches<'static> {
.arg(Arg::with_name("search") .arg(Arg::with_name("search")
.short("s") .short("s")
.long("search") .long("search")
.value_name("REPO") .value_names(&["REPO"])
.help("Search repositories for a user") .help("Search repositories for a user")
) )
.arg(Arg::with_name("list") .arg(Arg::with_name("list")

View File

@ -1,14 +1,15 @@
use std::env; use std::env;
use config::File; use config::File;
use serde::{Deserialize}; use serde::Deserialize;
#[derive(Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
pub struct Configuration { pub struct Configuration {
pub api_token: String, pub api_token: Option<String>,
pub base_api: String, pub base_api: String,
pub base_url: String, pub base_url: String,
pub username: Option<String> pub username: Option<String>,
pub password: Option<String>
} }
impl Configuration { impl Configuration {

View File

@ -7,21 +7,25 @@ mod arg;
mod config; mod config;
mod issue; mod issue;
mod repo; mod repo;
mod request;
use clap::{ArgMatches}; use clap::ArgMatches;
fn main() { fn main() {
let matches: ArgMatches = arg::get_args(); let mut matches: ArgMatches = arg::get_args();
let config = crate::config::Configuration::new(); let mut config = crate::config::Configuration::new();
match matches.subcommand() { let auth = request::Authentication::new(&config);
let request = auth.request_chooser(config.clone(), matches);
match request.arg_value.subcommand() {
("", None) => println!("No subcommand was given!"), ("", None) => println!("No subcommand was given!"),
("repo", Some(repo_matches)) => { ("repo", Some(repo_matches)) => {
let repo = repo::Repository::new(); let repo = repo::Repository::new();
// TODO: match expression should be here // TODO: match expression should be here
if repo_matches.is_present("create") { if repo_matches.is_present("create") {
repo.create_repo(&config, repo_matches); repo.create_repo(&request);
} }
if repo_matches.is_present("delete") { if repo_matches.is_present("delete") {
@ -51,3 +55,4 @@ fn main() {
_ => println!("Huh?") _ => println!("Huh?")
} }
} }

View File

@ -6,6 +6,7 @@ use reqwest::{StatusCode, Client};
use serde_derive::{Serialize, Deserialize}; use serde_derive::{Serialize, Deserialize};
use crate::config::Configuration; use crate::config::Configuration;
use crate::request::Request;
pub struct Repository; pub struct Repository;
@ -102,14 +103,17 @@ impl Repository {
Repository {} Repository {}
} }
pub fn create_repo(&self, config: &Configuration, arg: &ArgMatches) { pub fn create_repo(&self, request: &Request) {
let client = Client::new(); let client = &request.client;
let arg_value = arg.value_of("create").unwrap(); let arg_value = request.arg_value.subcommand().1.unwrap().value_of("create").unwrap();
let mut map: HashMap<&str, &str> = HashMap::new(); let mut map: HashMap<&str, &str> = HashMap::new();
let url = format!("{base_url}{base_api}/user/repos?token={api_token}", let url = format!("{request}/user/repos?token={api_token}",
base_url = config.base_url, request = request.url.as_ref().unwrap(),
base_api = config.base_api, api_token = request
api_token = config.api_token); .authentication
.credentials
.1.as_ref()
.unwrap());
map.insert("name", arg_value); map.insert("name", arg_value);
map.insert("readme", arg_value); map.insert("readme", arg_value);
@ -147,7 +151,7 @@ impl Repository {
base_api = config.base_api, base_api = config.base_api,
username = arg_iter[0], username = arg_iter[0],
repo_name = arg_iter[1], repo_name = arg_iter[1],
api_token = config.api_token); api_token = config.api_token.as_ref().unwrap());
let response = client.delete(&url) let response = client.delete(&url)
.send(); .send();
@ -182,7 +186,7 @@ impl Repository {
base_api = config.base_api, base_api = config.base_api,
owner = arg_item[0], owner = arg_item[0],
repo = arg_item[1], repo = arg_item[1],
api_token = config.api_token); api_token = config.api_token.as_ref().unwrap());
map.insert("name", user.as_str()); map.insert("name", user.as_str());
@ -216,7 +220,7 @@ impl Repository {
base_url = config.base_url, base_url = config.base_url,
base_api = config.base_api, base_api = config.base_api,
query = arg_value, query = arg_value,
api_token = config.api_token); api_token = config.api_token.as_ref().unwrap());
let response = client.get(url.as_str()) let response = client.get(url.as_str())
.send(); .send();
@ -255,7 +259,7 @@ impl Repository {
let url = format!("{base_url}{base_api}/repos/search?token={api_token}", let url = format!("{base_url}{base_api}/repos/search?token={api_token}",
base_url = config.base_url, base_url = config.base_url,
base_api = config.base_api, base_api = config.base_api,
api_token = config.api_token); api_token = config.api_token.as_ref().unwrap());
let response = client.get(url.as_str()) let response = client.get(url.as_str())
.send(); .send();

View File

@ -7,28 +7,55 @@ use crate::config::Configuration;
pub struct Request<'a> { pub struct Request<'a> {
pub client: Client, pub client: Client,
pub arg_value: Vec<&'a str>, pub arg_value: ArgMatches<'a>,
pub map: HashMap<String, String>, pub map: HashMap<String, String>,
pub url: Option<String>, pub url: Option<String>,
pub authentication: Authentication,
} }
pub struct Authentication { pub struct Authentication {
pub basic: bool, pub basic: bool,
pub api_token: bool, pub api_token: bool,
pub credentials: (Option<String>, Option<String>)
} }
impl Request<'static> { impl<'a> Request<'a> {
fn new() -> Request<'static> { /// Public constructor for a request with a simple username and password
pub fn with_basic_request(
config: Configuration,
arg: ArgMatches,
auth: Authentication
) -> Request {
Request { Request {
client: Client::new(), client: Client::new(),
arg_value: Vec::new(), arg_value: arg,
map: HashMap::new(), map: HashMap::new(),
url: None url: Some(format!("{:?}{:?}", config.base_url, config.base_api)),
authentication: auth,
}
}
/// Public constructor for a request with an API token
pub fn with_api_request(
config: Configuration,
arg: ArgMatches,
auth: Authentication
) -> Request {
Request {
client: Client::new(),
arg_value: arg,
map: HashMap::new(),
url: Some(format!("{:?}{:?}", config.base_url, config.base_api)),
authentication: auth,
} }
} }
} }
impl Authentication { impl Authentication{
/// Public constructor for getting authentication, provided by the configuration
/// file. The most secure methods are checked first, filtering down to the least
/// secure methods. Currently, only two methods are supported: API token, and
/// username/password combo.
pub fn new(config: &Configuration) -> Authentication { pub fn new(config: &Configuration) -> Authentication {
let basic_auth: String; let basic_auth: String;
let api_auth: String; let api_auth: String;
@ -37,28 +64,67 @@ impl Authentication {
// this is horror code, I know it // this is horror code, I know it
// match the damn thing // match the damn thing
// someone is going to take this and put it in r/badcode lol // someone is going to take this and put it in r/badcode lol
basic_auth = config
.password
.as_ref()
.unwrap()
.to_string();
match config { api_auth = config
_ => panic!() .api_token
.as_ref()
.unwrap()
.to_string();
if api_auth.is_empty() {
if !(basic_auth.is_empty()) {
Authentication::with_basic(config.username.as_ref().unwrap().to_string(), basic_auth)
} else {
panic!("Must have some form of authentication! Exiting...");
}
} else {
Authentication::with_api_token(api_auth)
} }
} }
fn with_basic() -> Authentication { /// Private constructor once the public constructor figures out what kind of authentication
/// is being used. This constructor uses the username/password combo, a less secure of
/// authenticating that the API token.
fn with_basic(user: String, pass: String) -> Authentication {
Authentication { Authentication {
basic: true, basic: true,
api_token: false api_token: false,
credentials: (Some(user), Some(pass))
} }
} }
fn with_api_token() -> Authentication { /// Private constructor once the public constructor figures out what kind of authentication
/// is being used. This constructor uses the API token, a more secure way of authenticating
/// instead of using the basic username and password.
fn with_api_token(api_token: String) -> Authentication {
Authentication { Authentication {
basic: false, basic: false,
api_token: true api_token: true,
credentials: (Some(api_token), None)
} }
} }
// This method requires the instanciated config, an optional instanciated args, and instanciated auth /// Public method that based on the what kind of authentication is being used, it can
pub fn request_chooser(&self, config: &Configuration, arg: Option<&ArgMatches>, auth: &Authentication, arg_string: &str) { /// determine what kind of requesting method is going to be used. See the Request
/// structure for more details.
pub fn request_chooser(
self,
config: Configuration,
arg: ArgMatches<'static>,
) -> Request {
if let true = self.api_token {
Request::with_api_request(config, arg.to_owned(), self)
} else {
match self.basic {
true => Request::with_basic_request(config, arg.to_owned(), self),
false => panic!(),
}
}
} }
} }